本章内容
- 异常
- 线程
异常
异常的概念
异常:不正常,有毛病。程序当中的异常指的是出现的不正常的情况,会导致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
如果异常出现的话,会立刻终止程序
- 如果使用throws关键字来声明式处理,由该方法的调用者来处理
- 在方法中使用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官方提供的异常类始终是有限的,大多数情况下我们需要根据自己的业务需要来定义异常类,例如:年龄负数异常,成绩负数,登录异常,注册异常等等
什么是自定义异常类?
在开发中根据自己的业务的异常情况来定义异常类
我想自定义一个业务逻辑异常:注册异常
如何定义?
- 自定义一个编译期异常,自定义类继承于java.lang.Exception
- 自定义一个运行期异常类,自定义类继承于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;
}
}