一、异常的产生
1.1 异常与错误的概念:
java.lang.Throwable:类是java语言中所有错误的超类。
- Exception:编译期异常,进行编译(写代码)Java程序出现的问题。
RuntimeException:运行期异常。
异常相当于程序得了小毛病,处理掉就可以继续执行。 - Error:错误,相当于程序得了一个无法治愈的毛病,必须修改源代码,程序才能继续执行。
1.2 Exception
exception例子:
parse是一个有异常的方法,执行之前必须抛出异常。
1、交给虚拟机处理(中断处理,把异常打印在控制台)
public class demo01 {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //用来格式化日期
Date date = sdf.parse("1999-09-09"); //把字符串格式的日期解析为Date格式的日期。
}
}
问题:parse中日期格式与设定的相同,运行不会报异常,但是不相同的时候就会报异常。比如改为:Date date = sdf.parse(“1999-0909”);
2、try,catch处理
public class demo01 {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //用来格式化日期
Date date = null; //把字符串格式的日期解析为Date格式的日期。
try {
date = sdf.parse("1999-0909");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(date);
System.out.println("继续执行");
}
}
虽然控制台打印出异常,但是后续代码继续执行。
例子:
索引越界异常处理:
public class demo01 {
public static void main(String[] args) {
int[] arr = {1,2,3};
try{
System.out.println(arr[3]);
}catch (Exception e){
System.out.println(e);
}
System.out.println("继续执行");
}
}
1.3 Error
error例子:
public class demo02 {
/**
*OutOfMemoryError: Java heap space错误
*/
public static void main(String[] args) {
int[] arr = new int[1024*1024*1024];
}
}
内存溢出,创建的数组太大了,超出java分配的内存,必须修改源代码。
1.4 异常产生的过程
例子:
数组越界异常
public class demo02 {
/**
*OutOfMemoryError: Java heap space错误
* 内存溢出,创建的数组太大了,超出java分配的内存
*/
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(getElement(arr,3));
}
public static int getElement(int[] arr,int index){
int ele = arr [index];
return ele;
}
}
1、首先访问数组中的3索引,发现没有这个,JVM检测出异常,并判断出异常原因。然后getElement方法中也没有这个异常处理,JVM把异常对象抛给main方法。
2、main方法接收到这个对象,也没有异常处理,然后继续抛给JVM处理。
3、JVM接收到对象,打印出异常,并且终止正在运行的程序。
二、异常处理
java异常处理的五个关键字:try、catch、finally、throw、throws。
2.1 抛出异常throw
throw关键字:
- 作用:
可以使用throw关键字在指定的方法中抛出指定的异常。 - 使用格式:
throw new xxxException(“异常产生的原因”); - 注意:
1.1、throw必须写在方法内部。
1.2、throw关键字后边的new的对象必须是Exception或者Exception的子类对象。
1.3、throw关键字p抛出指定的异常对象,我们就必须处理这个异常对象。
*throw关键字后边创建的是RuntimeException或是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理。
*throw关键字后面创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try…catch。
例子:
1、我们先对传递过来的值进行判断是否为null。
public class demo03Throw {
public static void main(String[] args) {
int[] arr = null;
System.out.println(getElement(arr,0));
}
public static int getElement(int[] arr,int index){
/*
对传递过来的参数先进行校验
判断是否为null
*/
if ( arr == null){
throw new NullPointerException("传递的数组的值为空");
}
int ele = arr [index];
return ele;
}
}
2、判断传递的index是否不在数组索引的范围内
public class demo03Throw {
public static void main(String[] args) {
int[] arr = null;
int[] arr1 = {1,2,3};
//System.out.println(getElement(arr,0));
System.out.println(getElement(arr1,3));
}
public static int getElement(int[] arr,int index){
/*
对传递过来的参数先进行校验
判断是否为null
*/
if ( arr == null){
throw new NullPointerException("传递的数组的值为空");
}
/*
对index进行判断
判断是否不在数组索引的范围内
*/
if (index < 0 || index > arr.length-1){
throw new ArrayIndexOutOfBoundsException("传递的数组索引越界");
}
int ele = arr [index];
return ele;
}
}
2.2 Objects非空判断
例子:
public class demo04Objects {
public static void main(String[] args) {
method(null);
}
public static void method(Object o){
//对传递来的参数进行合法性判断
/*if (o == null){
throw new NullPointerException("传递的对象是空对象");
}*/
//Objects.requireNonNull(o);
Objects.requireNonNull(o,"传递的对象为null");
}
}
思想并没有怎么变,注意:Objects.requireNonNull的重载方法。
2.3 声明异常throws
throws关键字:异常处理的第一种方式,交给别人处理
- 作用:
*当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
*可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理,最终交给JVM处理。 - 使用格式:在方法声明中使用
修饰符:返回值类型 方法名(参数列表)
throws(){
throw new AaaException("产生原因");
throw new BbbException("产生原因");
...
}
- 注意:
*1、throws关键字必须写在方法声明处
*2、throws关键字后面声明的异常必须是Exception或者是Exception的子类
*3、方法内部如果抛出了多个异常对象,那么throws后面也必须声明多个异常。
*4、调用了一个声明抛出异常的方法,我们就必须处理声明的异常。要么继续使用throws交给JVM处理,要么try…catch。
例子:
*方法抛出一个异常
public class demo05Throws {
public static void main(String[] args) throws FileNotFoundException {
readFile("d:\\a.txt");
}
/*
定义一个方法,对传递的文件进行判断,在此固定文件路径。
*/
public static void readFile(String fileName) throws FileNotFoundException {
if (!fileName.equals("c:\\a.txt")){
throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
}
System.out.println("读取文件");
}
}
在readFile方法中抛出了异常,然后主方法调用readFile方法的时候还需要抛出异常。
*方法抛出多个异常
public class demo05Throws {
//FileNotFoundException extends IOException,直接声明IOException就可
public static void main(String[] args) throws FileNotFoundException,IOException {
readFile("c:\\a.xt");
}
//定义一个方法,对传递的文件进行判断,在此固定文件路径。
public static void readFile(String fileName) throws FileNotFoundException,IOException {
if (!fileName.equals("c:\\a.txt")){
throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
}
if (!fileName.endsWith(".txt")){
throw new IOException("文件后缀名不对");
}
System.out.println("读取文件");
}
}
FileNotFoundException extends IOException,直接声明IOException就可,可以把在方法上面抛出的FileNotFoundException 删除就行。
2.4 捕获异常try…catch
2.4.1 try…catch:自己处理捕获异常
try{
可能异常的代码
catch(定义一个异常的变量,接收try中抛出的异常对象){
异常处理的逻辑,产生异常对象之后怎么处理。
一般在工作中,会把异常的的信息记录日志
}
...
catch(异常类名 变量名)
注意:
1、try中抛出多个异常对象,那么用catch来处理这些异常
2、如果try产生了异常,那么就会执行catch中的异常逻辑,执行完try…catch之后继续执行后续代码。
例子:
public class demo06TryCatch {
public static void main(String[] args) {
try {
readFile("d:\\a.MP4");
}catch (IOException e){
System.out.println("传递的文件后缀不是.txt");
}
System.out.println("继续执行中...");
}
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith(".txt")){
throw new IOException("文件后缀名不对");
}
System.out.println("读取文件");
}
}
2.4.2 Throwable类定义的异常处理方法
1、String getMessage() 返回此throwable的简短描述
2、String toString() 返回此throwable的详细消息字符串
3、void printStackTrace() JVM打印异常对象,默认此方法,异常信息是最全面的。
例子:
public class demo06TryCatch {
public static void main(String[] args) {
try {
readFile("d:\\a.MP4");
}catch (IOException e){
//System.out.println("传递的文件后缀不是.txt");
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
System.out.println("继续执行中...");
}
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith(".txt")){
throw new IOException("文件后缀名不对");
}
System.out.println("读取文件");
}
}
2.5 finally代码块
finally代码块:
- 无论程序出不出异常,我们都需要执行这段代码。
- 一般用于资源释放。
- x
例子:
public class demo07TryCatchFinally {
public static void main(String[] args) {
try {
//可能会产生异常的代码
readFile("c:\\a.xt");
} catch (IOException e) {
//异常的处理逻辑
e.printStackTrace();
}finally {
System.out.println("资源释放");
}
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith(".txt")){
throw new IOException("文件后缀名不对");
}
System.out.println("读取文件");
}
}
2.6 异常的注意事项
- 多个异常使用捕获又该如何处理呢?
1.多个异常分别处理。
public class demo08Exception {
public static void main(String[] args) {
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}
try {
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list.get(3));
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
System.out.println("继续执行...");
}
}
2.多个异常一次捕获,多次处理。
public class demo08Exception {
public static void main(String[] args) {
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list.get(3));
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
System.out.println("继续执行...");
}
}
一个try多个catch注意:
- catch里面定义的异常变量,如果有子父关系,那么子类的异常变量必须写在上面,否正就会报错。
- 比如ArrayIndexOutOfBoundsException继承了IndexOutOfBoundsException,将ArrayIndexOutOfBoundsException的catch放在下面,就会报错。原因就是继承关系,下面写了一句废话,最高级的就是Exception。
3.多个异常一次捕获,一次处理。
public class demo08Exception {
public static void main(String[] args) {
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list.get(3));
}catch (Exception e){
System.out.println(e);
}
System.out.println("继续执行...");
}
}
一般使用一次捕获多次处理的方式,格式如下:
try{
编写可能会出现异常的代码
}catch(异常类型A e){ 当try中出现A类型的异常,就用该catch来捕获
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型B e){ 当try中出现B类型的异常,就用该catch来捕获
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
- 运行时异常被抛出可以不处理,即不捕获也不声明抛出。直接给虚拟机处理。
- 如果finally有return语句,永远返回finally中的结果,避免该情况。
*finally通常用于资源回收
public class demo09Finally {
public static void main(String[] args) {
System.out.println(getA());
}
public static int getA(){
int a = 10;
try {
return a;
}catch (Exception e){
System.out.println(e);
}finally {
a = 100;
return a;
}
}
}
- 如果父类抛出了多个子异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子类或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类该方法的时候也不抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
public class Fu {
public void show01() throws NullPointerException,ClassCastException{}
public void show02() throws IndexOutOfBoundsException{}
public void show03() throws IndexOutOfBoundsException{}
public void show04(){}
}
class Zi extends Fu{
//子类重写父类方法的时候,抛出和父类相同的异常。
public void show01() throws NullPointerException,ClassCastException{}
//子类重写父类方法的时候,抛出父类异常的子类
public void show02() throws ArrayIndexOutOfBoundsException{}
//子类重写父类方法时候,不抛出异常。
public void show03(){}
//父类方法没有抛出异常,子类也不可以抛出异常。
///public void show04()throws Exception{}
//此时子类产生的异常只能捕获处理。
public void show04(){
try {
throw new Exception("编译期异常");
}catch (Exception e) {
e.printStackTrace();
}
}
}
2.7 自定义异常类
自定义异常类:
- java提供的异常类不够使用。
格式:
public class xxxException extends Exception | RuntimeException{
添加一个空参的构造方法。
添加一个异常信息的构造方法
}
- 注意:
*自定义异常类一般都是Exception结尾,说明该类是一个异常类。
*必须继承的是Exception 或者 RuntimeException类
*继承Exception:自定义的异常类就是一个编译期异常类
*继承RuntimeException:就是一个运行期异常类,无需处理,交个给虚拟机处理做中断处理。
例子:
public class registerException extends Exception {
//添加一个空参构造方法
public registerException(){
}
/*
添加一个带异常信息方法,
查看源码发现,所有的异常类都有一个异常信息的构造方法,
方法内部会调用父类带异常信息的构造方法,让父类来处理.
*/
public registerException(String message){
super(message);
}
}
练习:
要求:模拟用户注册,如果用户名已经存在,则抛出异常并提示:该用户已经被注册。
分析:
- 使用数组保存已经注册过的用户名。
- 用Scanner获取用户注册的用户名。
- 定义一个方法,对用户输入注册的用户名进行判断。
- 遍历存储已经注册过用户名的数组,获取每一个用户名。
- 使用获取到的用户名和用户输入的用户名机比较
*true:用户名已经存在,抛出异常。
*false:继续遍历
*如果循环结束没找到重复的,提示注册成功。
方法一:使用throws抛出异常
定义异常类:registerException
public class registerException extends Exception {
//添加一个空参构造方法
public registerException(){
}
//添加一个带异常信息方法,
//查看源码发现,所有的异常类都有一个异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理
public registerException(String message){
super(message);
}
}
创建registerMyException调用registerException
public class registerMyException {
//使用数组保存数据
static String[] usernames = {"张三","李四","王五"};
public static void main(String[] args) throws registerException {
//使用Scanner获取输入的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要注册的用户名:");
String username = sc.next();
checkUserName(username);
}
//定义一个方法,对用户输入的用户名进行判断
public static void checkUserName(String username) throws registerException {
for (String name : usernames) {
//使用获取到的用户名与现有的比较
if (name.equals(username)){
//用户名已经存在,抛出异常
throw new registerException("该用户已经被注册");
}
}
//如果循环结束没找到重复的,提示注册成功。
System.out.println("恭喜您,注册成功!!!");
}
}
方法二:使用try…catch捕获异常…代码就不放了很简单。