一、异常
1、异常的概念:
异常指的是不正常, 指的是程序中出现了某些问题。
在Java中,所有的问题都可以使用一个类来表示,这个类叫做Throwable。
Throwable是Java中所有异常和错误的父类。
Throwable
|-- Error: 表示错误。 指的是不可挽回的严重问题。 相当于人得了绝症。
|-- Exception:表示异常, 指的是可以挽回的轻微问题。 相当于人得了感冒,咳嗽。
2、异常产生的过程
public class Demo02Exception {
public static void main(String[] args) {
//定义一个数组
int[] arr = new int[2];
//调用getValue方法,获取数组中索引为10的元素
int value = getValue(arr);
System.out.println("value:" + value);
}
/*
定义方法,用来获取数组中某个索引上的元素
*/
public static int getValue(int[] arr) {
int value = arr[10]; //JVM会自动创建一个异常对象,然后自动抛出.
return value;
}
}
3、throw 关键字
如果想要手动的创建一个异常并且手动的抛出去,那么我们就可以使用throw关键字。
格式:
throw new 异常类名();
注意:
如果在某个方法内抛出异常,那么这个方法剩下的代码就都不会执行。
public class Demo03Throw {
public static void main(String[] args) {
//创建数组
int[] arr = new int[2];
int value = getValue(arr, 10);
System.out.println("value:" + value);
}
/*
定义方法,获取数组中指定索引的数据
*/
public static int getValue(int[] arr, int index) {
//在JVM自动抛出异常之前,我们对索引的取值范围进行检测, 如果索引的取值范围不合法,那么我们就手动抛出一个异常
//索引的取值范围是: 0 ~ arr.length-1
if(index < 0 || index > arr.length - 1) {
//如果索引的取值范围不合法,那么我们就手动创建一个ArrayIndexOutOfBoundsException,并且手动抛出。
//如果想要创建异常对象的使用给这个异常带上异常信息,那么可以在异常的构造方法位置传递异常信息
String msg = "索引的取值范围必须是0到" + (arr.length - 1) + ", 您给的索引为:" + index;
throw new ArrayIndexOutOfBoundsException(msg);
}
System.out.println("你好");
return arr[index];
}
}
4、Objects中的requireNonNull
通过null去调用任何的属性和方法都会引发空指针异常
public class Demo04Exception {
public static void main(String[] args) {
//定义一个Object
Object obj = null;
//调用toString方法之前判断,如果obj是null,那么手动抛出一个异常
/*
if (obj == null) {
throw new NullPointerException();
}
*/
Objects.requireNonNull(obj, "程序出现了空指针");
obj.toString();//当代码执行到这一行,JVM会发现通过null调用了方法,那么此时JVM就会自动创建一个异常对象并抛出.
}
}
5、throw: 手动的抛出一个异常。
throws: 进行异常声明, 声明方法有可能会抛出异常。
格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类名{
}
注意:
1. 如果在一个方法内抛出了编译时异常,那么一定要进行异常的声明
2. 如果调用了使用throws声明异常的方法,那么作为调用者要么处理异常(try..catch),要么也使用throws声明这个方法会抛出异常
3. 当一个方法内抛出了运行时异常,可以使用throws进行异常声明,也可以不声明。
4. 在一个方法内如果可能抛出多个异常,那么可以使用throws声明这些异常,也可以直接声明这些异常的父类异常。
public class Demo05Throws {
public static void main(String[] args) throws IOException{
method1();
}
public static void method1() throws IOException{
throw new IOException();
}
public static void method2() {
throw new NullPointerException();
}
public static void method3() throws Exception{
int i = 0;
if(i % 2 == 0) {
throw new IOException();
} else {
throw new ClassNotFoundException();
}
}
}
6、
如果代码出现异常之后不希望把这个异常往外,那么我们可以使用try...catch处理解决这个异常。
格式:
try {
可能会出现异常的代码;
} catch(要捕获的异常类名 变量名) {
出现异常后执行的代码。
}
执行流程:
如果try中代码没有问题,那么执行完try之后,会跳过catch继续往下执行。
如果try中的代码有问题,并且catch捕获到了这个异常,那么就会直接进入到catch中执行。
如果try中的代码有问题,但是catch没有捕获到这个异常,那么这个异常会依旧往外抛。
异常处理有两种方式:
1. 往外抛(甩锅): throw throws
2. 解决问题: try...catch
public class Demo06TryCatch {
public static void main(String[] args) {
try {
Object obj = null;
obj.toString();
System.out.println("1. try...end");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("2. catch代码...");
}
System.out.println("3. main...end");
}
}
7、try...catch...finally
格式:
try {
【A】可能出现异常的代码
} catch(要捕获的异常类 变量名) {
【B】出现异常后执行的代码
} finally {
【C】一定会执行的代码
}
finally 代码块无论如何都会执行。
执行流程:
如果try中的代码没有出现问题,那么执行流程为: 【A】【C】
如果try中的代码有问题,并且catch捕获到了,执行流程为: 【A】【B】【C】
如果try中的代码有问题,但是catch没有捕获到, 执行流程为: 【A】【C】 抛出异常
finally 代码块的作用一般用于释放资源。 比如后期IO流释放资源,JDBC释放连接等等。
public class Demo07TryCatchFinally {
public static void main(String[] args) {
try {
System.out.println("1. try...start");
Object obj = null;
obj.toString();
System.out.println("2. try...end");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("3. catch");
} finally {
System.out.println("4. finally");
}
}
}
8、如果在方法内抛出了运行时异常,那么我们可以处理,也可以不处理。 如果不处理假如代码运行到这里,这个异常依旧会抛给调用者
如果在方法内抛出了编译时异常,那么必须要处理。 要么try...catch解决掉问题。 要么使用throws声明这个方法会抛出异常。
总结:
运行时异常可以处理,也可以不处理。 编译时异常必须处理。
public class Demo08Exception {
public static void main(String[] args) {
method1();
}
public static void method1() {
throw new RuntimeException();
}
public static void method2() {
try {
throw new Exception();
} catch (Exception e) {
}
}
}
9、在try...catch处理异常时,可以写多个catch去捕获多个异常。
try {
可能会出现问题的代码
} catch(要捕获的异常 变量名) {
出现异常执行的代码;
} catch(要捕获的异常 变量名) {
出现异常执行的代码;
} catch(要捕获的异常 变量名) {
出现异常执行的代码;
}
如果try中的代码出现了问题,哪个catch先捕获到,那么就执行哪个catch中的代码。 剩下的catch都不在执行了
注意:
如果有多个catch,那么捕获的父类异常不能写在子类异常的前面
public class Demo09TryCatchCatch {
public static void main(String[] args) {
try {
Object obj = null;
obj.toString();
System.out.println("1. try");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("2. ArrayIndexOutOfBoundsException");
} catch (NullPointerException e) {
System.out.println("3. NullPointerException");
} catch (ClassCastException e) {
System.out.println("4. ClassCastException");
} catch (Exception e) {
System.out.println("5. Exception");
}
}
}
10、finally关键字
public class Demo10TryCatchFinally {
public static void main(String[] args) {
int value = method();
System.out.println("value:" + value);
}
/*
1. 当执行到try中的return的时候,会先把要返回的值给保存起来,留作以后返回结果的时候使用。 要返回的结果是10
2. 因为后面有finally代码块,所以并不能直接返回,必须先要执行完finally代码块才能返回。 此时去执行finally
3. 执行finally的时候把i的值变成了20.
4. 执行完finally之后,再返回结果,返回的是之前已经保存好的10
*/
public static int method() {
int i = 10;
try {
return i;//10
} catch (Exception e) {
System.out.println("catch");
} finally {
i = 20;
System.out.println("finally....");
}
return 0;
}
}
11、Throwable中的常用方法:
String toString(): 返回异常信息的详细描述
String getMessage(): 返回异常信息的简短描述
void printStackTrace():将异常信息打印到控制台。 (最详细)
上面这些方法不是用来创建对象后直接调用的, 这些方法要用在catch中。
当catch捕获到异常对象后,可以通过上面的方法获取到异常的信息。
public class Demo11ThrowableMethod {
public static void main(String[] args) {
try {
int[] arr = new int[2];
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) { //异常对象其实是用变量e捕获的,所以这些方法要通过变量e去调用
//System.out.println(e.toString()); //java.lang.ArrayIndexOutOfBoundsException: 10
//System.out.println(e.getMessage()); //10
e.printStackTrace();
}
System.out.println("end...");
}
}
12、
在继承关系中,方法重写时异常的注意事项
如果父类方法中抛出了异常,子类方法可以抛,也可以不抛。 如果子类方法往外抛, 要么和父类方法的异常相同,要么是父类方法异常的子类异常
如果父类方法没有抛出异常,那么子类方法在重写的时候也不能抛。只能try...catch
上面的限制只适用于编译时异常,并不适用于运行时异常。
package cn.itcast.demo01;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
在继承关系中,方法重写时异常的注意事项
如果父类方法中抛出了异常,子类方法可以抛,也可以不抛。 如果子类方法往外抛, 要么和父类方法的异常相同,要么是父类方法异常的子类异常
如果父类方法没有抛出异常,那么子类方法在重写的时候也不能抛。只能try...catch
上面的限制只适用于编译时异常,并不适用于运行时异常。
*/
public class Zi extends Fu{
@Override
public void method() throws FileNotFoundException{
}
@Override
public void function() {
String str = "2011-11-11";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(str);
} catch (ParseException e) {
}
System.out.println(date);
}
}
package cn.itcast.demo01;
import java.io.IOException;
public class Fu {
public void method() throws IOException{
}
public void function() {
}
}
13、自定义异常
如何自定义异常?
认贼作父
如果定义一个类,去继承一个异常类,那么这个类也就变成了异常类。
如果这个类继承的是编译时异常类,那么我们自定义的这个异常就是编译时异常。
如果这个类继承的是运行时异常类,那么我们定义的这个异常,就是运行时异常。
public class RegistException extends RuntimeException{
//定义有参构造,用来给出异常信息
public RegistException(String msg) { //msg = "注册失败"
//调用父类构造方法,传递异常信息
super(msg);
}
}
package cn.itcast.demo02;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Demo01Regist {
//定义一个数组,当做数据库,里面保存的是已经存在的用户名
static String[] users = {"jack", "rose", "tom"};
public static void main(String[] args) {
//定义一个要注册的用户名
String username = "rose";
//调用regist方法注册
//regist方法如果注册失败,在这个方法内部会抛出异常。
//如果注册成功,就不会抛出。
//所以可以使用trycatch捕获处理异常,如果没有捕获到异常,表示注册成功
//如果捕获到了异常,就表示注册失败,说明这个方法内部遇到问题
try {
regist(username);
System.out.println("注册成功");
} catch (RegistException e) {
System.out.println(e.getMessage());//获取到简短的异常信息并直接打印到控制台
} catch (Exception e) { //为了保证程序的健壮性,进行捕获,捕获漏掉的异常
System.out.println("出现了其他异常");
}
}
/*
定义一个方法,用来进行注册。
参数传递要注册的用户名。
在方法内进行判断,如果该用户名已经注册了,那么就直接抛出异常
如果没有被注册,就不抛出异常,表示整个过程没有问题。
*/
public static void regist(String username) {
//判断users这个数组中是否包含我们要注册的用户名,如果保存,表示已经存在,那么就注册失败(抛出异常)
//遍历users,拿users中的每一个元素和要注册的用户名进行比较
for(String thisUser : users) {
//thisUser表示的就是users中的每一个用户名
//如果我们要注册的用户名和遍历到的用户名相同,那么表示已经存在。
if(username.equals(thisUser)) {
//如果用户名已经存在,那么抛出异常,表示过程出现了问题
//throw new RuntimeException("注册失败");
throw new RegistException("注册失败");
}
}
}
}