异常
概念
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
体系
异常的种类
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
初体验
抛出异常
public static void main(String[] args) throws ParseException {
//Exception:编译器异常,进行编译(写代码)java程序出现问题
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");//格式化日期
Date parse = format.parse("2001-12-02");
System.out.println(parse);
}
捕获异常
public static void main(String[] args){
//Exception:编译器异常,进行编译(写代码)java程序出现问题
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");//格式化日期
Date parse = null;
try {
parse = format.parse("2001-12-02");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(parse);
}
运行时异常
public static void main(String[] args) {
//运行时异常
int[] a = {1,2,3};
System.out.println(a[3]);//ArrayIndexOutOfBoundsException 数组下标越界异常
}
错误
public static void main(String[] args) {
/*
Error
内存溢出的错误,创建的数组太大了
*/
int[] a = new int[1024*1024*1024];//OutOfMemoryError
System.out.println("123");
}
异常产生的过程解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mMODXYbA-1629538844552)(D:\aazhuomian\新建文件夹\Java语言基础课程8.8.assets\image-20210821094105580.png)]
异常的处理
第一种方式:throw
介绍
**作用:**可以使用throw关键字在指针的方法中抛出指定的异常
使用格式:
throw new xxxException("异常产生的原因")
注意:
throw关键字必须写在方法的内部
throw关键字后面new的对象必须是Exception或者子类
throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
Objects非空判断
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
举例
public class ThrowDemo {
public static void main(String[] args) {
//判断是否为空
method(null);
}
private static void method(Object o) {
//对传过来的值进行判断,判断是否为空
// if (o == null){
// throw new NullPointerException("为空");
// //Exception in thread "main" java.lang.NullPointerException: 为空
// }
//Objects.requireNonNull(o);
Objects.requireNonNull(o,"为空");
}
第二种方式声明异常Throws
介绍
throws关键字:异常处理的第一种方式,交给别人处理
作用:
当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理
使用格式:在方法声明时使用
修饰符 返回值类型 方法名(参数列表) throws AAAExcepiton,BBBExcepiton...{
throw new AAAExcepiton("产生原因");
throw new BBBExcepiton("产生原因");
...
}
注意:
1.throws关键字必须写在方法声明处
2.throws关键字后边声明的异常必须是Exception或者是Exception的子类
3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常
如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
4.调用了一个声明抛出异常的方法,我们就必须的处理声明的异常
要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM
要么try…catch自己处理异常
举例
1
public static void main(String[] args) throws FileNotFoundException {
readFile("c:\\a.tx");
//Exception in thread "main" java.io.FileNotFoundException: 传递文件路径不正确
}
/**
* 定义一个方法,对传递的文件路径进行合法性判断
* 如果路径不是"c:\\a.txt",那么我们就抛出文件找不到异常对象,告知方法的调用者
* 注意:
* FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
* 可以使用throws继续声明抛出FileNotFoundException这个异常对象,让方法的调用者处理
* @param s
* @throws FileNotFoundException
*/
private static void readFile(String s) throws FileNotFoundException {
if (!s.equals("c:\\a.txt")){
throw new FileNotFoundException("传递文件路径不正确");
}
System.out.println("传递文件路径正确");
}
2
public static void main(String[] args) throws IOException {
readFile1("c:\\a.tx");
//Exception in thread "main" java.io.FileNotFoundException: 传递文件路径不正确
}
/**
* 如果传递的路径,不是.txt结尾
* 那么我们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对
* @param s
*/
private static void readFile1(String s) throws IOException {
if (!s.endsWith(".txt")){
throw new IOException("文件的后缀名不对");
}
System.out.println("读取文件");
}
//Exception in thread "main" java.io.IOException: 文件的后缀名不对
第三种方式 捕获异常try…catch
try…catch:异常处理的第二种方式,自己处理异常
格式:
try{
可能产生异常的代码
}catch(定义一个异常的变量,用来接收try中抛出的异常对象){
异常的处理逻辑,异常异常对象之后,怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}
...
catch(异常类名 变量名){
}finally{
无论是否出现异常都会执行
注意:
finally不能单独使用,必须和try一起使用
finally一般用于资源回收,无论程序是否出现异常,最后都要资源释放(IO)
}
注意:
1.try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
2.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码
3.如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码
举例
public static void main(String[] args) {
int i = 5;
try {
i = i/0;
}catch (ArithmeticException e){
System.out.println("除数不能为0");
}finally {
System.out.println("我执行了,谁也不能阻止我");
}
}
finally中的return
如果finally有return语句,永远返回finally中的结果,避免该情况
public class FinallyDemo {
public static void main(String[] args) {
System.out.println(a());
}
public static int a(){
int a = 10;
try {
return a;
}catch (Exception e){
System.out.println(e);
}finally {
a = 1000;
return a;
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCsjWUnR-1629538844558)(D:\aazhuomian\新建文件夹\Java语言基础课程8.8.assets\image-20210821104511941.png)]
自定义异常类
java提供的异常类,不够我们使用,需要自己定义一些异常类
格式:
public class XXXExcepiton extends Exception | RuntimeException{
添加一个空参数的构造方法
添加一个带异常信息的构造方法
}
注意:
1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类
2.自定义异常类,必须的继承Exception或者RuntimeException
继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch
继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
public class ExceptionDemo extends Exception{
public ExceptionDemo() {
}
public ExceptionDemo(String message) {
super(message);
}
}
练习:判断用户是否存在
**要求:**我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。
分析:
1.使用数组保存已经注册过的用户名(数据库)
2.使用Scanner获取用户输入的注册的用户名(前端,页面)
3.定义一个方法,对用户输入的中注册的用户名进行判断
遍历存储已经注册过用户名的数组,获取每一个用户名
使用获取到的用户名和用户输入的用户名比较
true:
用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
false:
继续遍历比较
如果循环结束了,还没有找到重复的用户名,提示用户"恭喜您,注册成功!";
public class Practice {
//1.使用数组保存已经注册过的用户名(数据库)
static String[] username = {"wang","xu","yang"};
public static void main(String[] args) throws ExceptionDemo {
System.out.println("输入的注册的用户名");
//使用Scanner获取用户输入的注册的用户名(前端,页面)
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
checkUsername(name);
}
private static void checkUsername(String name) throws ExceptionDemo {
for (String s : username) {
//用户获取到的用户名与输入的用户进行比较
if (name.equals(s)){
throw new ExceptionDemo("该用户名已经被注册");
}
}
System.out.println("注册成功");
}
}