1异常介绍
2异常体系和分类
常见的运行期异常:
NullPointerException:空指针异常,一般是使用null调用了方法,访问属性导致
IndexOutOfBoundsException:索引越界异常(集合会报),超出了集合索引的使用范围
StringIndexOutOfBoundsException:字符串索引越界异常,超出了字符串索引的使用范围
ArrayIndexOutOfBoundsException:数组索引越界异常,超出了数组索引的使用范围
ClassCastException:类型转换异常 向下转型的时候会抛出
ConcurrentModificationException:迭代器并发修改异常 迭代器|增强for遍历集合同时修改集合长度会报
NoSuchElementException:没有元素异常
NumberFormatException:数字格式化异常
IllegalThreadStateException:非法状态异常 线程对象重复的调用start方法
package com.itheima.demo01Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
异常的体系和分类
*/
public class Demo01 {
public static void main(String[] args) {
//java.lang.Exception extends Throwable类:编译期异常,在写代码的时候,程序报的异常
/*try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2022-324");
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}*/
//java.lang.RuntimeException extends Exception:运行期(时)异常,在运行代码的时候,程序报的异常
/*try {
int[] arr = {10,20,30};
System.out.println(arr[3]);
} catch (Exception e) {
e.printStackTrace();
}*/
/*
-- java.lang.Error extends Throwable类 :错误
OutOfMemoryError: Java heap space:内存溢出的错误
创建的数组太大了,超出了内存的使用范围(内存装不下了)
*/
//int[] arr = new int[1000*1000*1000];
int[] arr = new int[1000*1000];
System.out.println("后边还有100行代码要执行!");
}
}
3.异常的产生过程解析(面试)
4.throw关键字(重点)
throw关键字使用
作用:
让我们在[方法中]抛出指定的异常对象
没有使用throw关键字,程序执行的过程中出现了异常,JVM会创建异常相关的对象,并抛出异常对象
格式:
修饰符 返回值类型 方法名(参数列表){
throw new xxxException(“异常的错误信息”);
…
throw new yyyException(“异常的错误信息”);
}
注意:
1.throw关键字必须写在[方法中]
2.throw关键字后边创建的对象,必须是Exception或者RuntimeException或者他们的子类
不能是和异常无关的对象(Person,Student==>错误)
3.throw关键字抛出的是运行时(期)异常,我们无需处理异常,默认交给JVM处理(中断)
throw关键字抛出的是编译期异常,我们就必须的处理这个异常
package com.itheima.demo01Exception;
/*
throw关键字使用
作用:
让我们在[方法中]抛出指定的异常对象
没有使用throw关键字,程序执行的过程中出现了异常,JVM会创建异常相关的对象,并抛出异常对象
*/
public class Demo03throw {
public static void main(String[] args) {
//int[] arr = null;
int[] arr = {10,20,30};
int element = getElement(arr, 3);
System.out.println(element);
}
/*
定义一个方法,获取数组指定索引处的元素并返回
在工作中一般都会对方法传递的实际参数做一些合法性的校验
参数合法,在使用参数
参数不合法,就可以使用抛出异常的方式,告之方法的调用者,参数有问题
*/
public static int getElement(int[] arr,int index){
/*
对参数int[] arr进行一个非空判断
如果数组arr的值null,那么我们就抛出一个空指针异常
告之方法的调用者,传递的数组arr的值是null
*/
if(arr==null){
throw new NullPointerException("您传递的数组arr的值是null");
}
/*
对参数int index数组索引,进行一个校验,判断index是否在数组索引范围内
如果index不在数组索引的范围内,那么我们就抛数组索引越界异常
告之方法的调用者,传递的数组的索引超出了范围
*/
if(index<0 ||index>arr.length-1){
throw new ArrayIndexOutOfBoundsException("您传递的数组索引["+index+"]超出了数组索引的范围");
}
int e = arr[index];
return e;
}
}
5.throws关键字(重点)
package com.itheima.demo01Exception;
/*
throws关键字使用
异常处理的第一种方式
作用:
throw关键字抛出的是编译期异常,我们就必须的处理这个异常
可以使用throws关键字把异常对象抛出给方法的调用者处理,最终可以抛出给JVM处理(中断)
弊端:
最终会把异常对象抛出给JVM,JVM会中断我们当前正在执行的程序
那么异常之后代码代码就不会执行了
*/
public class Demo04throws {
/*
main(String[] args) throws Exception main方法继续把异常对象Exception抛出给JVM处理
*/
public static void main(String[] args) throws Exception {
//int[] arr = null;
int[] arr = {10,20,30};
int element = getElement(arr, 3);
System.out.println(element);
System.out.println("后续100行代码");
}
/*
定义一个方法,获取数组指定索引处的元素并返回
getElement(int[] arr,int index) throws Exception 把异常对象Exception抛出给main方法处理
*/
public static int getElement(int[] arr,int index) throws Exception{
/*
对参数int[] arr进行一个非空判断
如果数组arr的值null,那么我们就抛出一个空指针异常
告之方法的调用者,传递的数组arr的值是null
*/
if(arr==null){
throw new Exception("您传递的数组arr的值是null");
}
/*
对参数int index数组索引,进行一个校验,判断index是否在数组索引范围内
如果index不在数组索引的范围内,那么我们就抛数组索引越界异常
告之方法的调用者,传递的数组的索引超出了范围
*/
if(index<0 ||index>arr.length-1){
throw new Exception("您传递的数组索引["+index+"]超出了数组索引的范围");
}
int e = arr[index];
return e;
}
}
6.throws抛出子父类异常的处理(重点)
在方法内部抛出了多个异常对象,有子父类关系,在方法上声明父类异常即可
package com.itheima.demo01Exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
在方法中抛出的多个异常对象,有子父类关系,在方法上声明父类异常对象即可
public class FileNotFoundException extends IOException extends Exception
main(String[] args) throws FileNotFoundException,IOException
简化为:
main(String[] args) throws IOException
main(String[] args) throws Exception
*/
public class Demo05throws {
/*
main(String[] args) throws FileNotFoundException,IOException 把两个异常对象甩给JVM处理
*/
public static void main(String[] args) throws Exception{
//readFile(null);
readFile("c:\\hello.java");
}
/*
定义一个方法,方法的参数传递一个文件的路径 d:\\abc.java
在方法中根据文件的路径,读取文件中的内容(IO流)
readFile(String path)throws FileNotFoundException,IOException 把两个异常对象甩给main方法处理
*/
public static void readFile(String path)throws FileNotFoundException,IOException{
/*
对文件的路径path进行合法性校验,判断path是否为null
IOException:读写异常
*/
if(path==null){
throw new IOException("传递的文件的路径是null");
}
/*
对文件的路径path进行合法性校验,判断path是否为d:\\abc.java
FileNotFoundException:文件找不到异常
*/
if(!path.equals("d:\\abc.java")){
throw new FileNotFoundException("传递的文件的路径不是d:\\abc.java");
}
//路径没有问题,读取文件
System.out.println("读取到了d:\\abc.java文件,文件中的内容是abc");
}
}
7.try…catch关键字(重点)
快捷键 选择中代码==>ctrl+alt+T==>选择try catch
package com.itheima.demo01Exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
try…catch异常处理
异常处理的第二种方式
作用
1.throw关键字抛出的是编译期异常,我们就必须的处理这个异常
2.调用了一个声明抛出编译期异常的方法,我们就必须的处理这个异常
就可以使用try catch关键字自己手动处理异常
好处:
使用try catch处理异常,可以继续执行try catch后边的代码
注意:
1.try中产生了异常对象,那么就会执行catch中异常的处理逻辑,执行完毕会继续执行try catch之后的代码
2.try中没有产生异常对象,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,会继续执行try catch之后的代码
快捷键 选择中代码==>ctrl+alt+T==>选择try catch
try:捕获
catch:接住
*/
public class Demo06tryCatch {
public static void main(String[] args) {
try {
//可能会产生异常的代码
readFile("d:\\abc.java");
} catch (FileNotFoundException e) {//FileNotFoundException e = new FileNotFoundException("传递的文件的路径不是d:\\abc.java");
//异常的处理逻辑:可以随意编写
System.out.println("哈哈");
}catch (IOException e) {//IOException e= new IOException("传递的文件的路径是null");
System.out.println("呵呵");
}
System.out.println("后续100行代码!");
}
public static void readFile(String path)throws FileNotFoundException,IOException {
if(path==null){
throw new IOException("传递的文件的路径是null");
}
if(!path.equals("d:\\abc.java")){
throw new FileNotFoundException("传递的文件的路径不是d:\\abc.java");
}
//路径没有问题,读取文件
System.out.println("读取到了d:\\abc.java文件,文件中的内容是abc");
}
}
8.Throwable类中定义的异常处理逻辑(了解)
Throwable类中定义的异常处理逻辑(了解)
java.lang.Throwable:异常和错误的父类,里边定义的方法所有的异常类都可以使用
String getMessage() 返回此可抛出的简短描述。
String toString() 重写Object类toString方法,返回此throwable的详细消息字符串。
void printStackTrace() JVM在控制台打印异常信息,默认调用的方法,打印异常信息最全面的
以上3个异常的处理方法,我们可以使用,也可以自定义异常的处理逻辑
Throwable类是Exception的父类
package com.itheima.demo01Exception;
/*
Throwable类中定义的异常处理逻辑(了解)
java.lang.Throwable:异常和错误的父类,里边定义的方法所有的异常类都可以使用
String getMessage() 返回此可抛出的简短描述。
String toString() 重写Object类toString方法,返回此throwable的详细消息字符串。
void printStackTrace() JVM在控制台打印异常信息,默认调用的方法,打印异常信息最全面的
以上3个异常的处理方法,我们可以使用,也可以自定义异常的处理逻辑
*/
public class Demo07Throwable {
public static void main(String[] args){
try {
throw new Exception("异常了");
} catch (Exception e) {//Exception e = new Exception("异常了");
//String getMessage() 返回此可抛出的简短描述。
String message = e.getMessage();
System.out.println(message);//异常了
//String toString() 重写Object类toString方法,返回此throwable的详细消息字符串。
String s = e.toString();
System.out.println(s);//java.lang.Exception: 异常了
System.out.println(e);//java.lang.Exception: 异常了
/*
void printStackTrace() JVM在控制台打印异常信息,默认调用的方法,打印异常信息最全面的
java.lang.Exception: 异常了
at com.itheima.demo01Exception.Demo07Throwable.main(Demo07Throwable.java:14)
*/
e.printStackTrace();
//以上3种打印异常信息的方法,都不使用,自定义异常的处理逻辑
System.out.println("哈哈终于出现了异常了"+e);//哈哈终于出现了异常了java.lang.Exception: 异常了
}
System.out.println("后续代码!");
}
}
9.finally关键字(重点)
finally关键字里边定义的代码,无论程序是否有异常,都会执行
finally必须和try一起使用
package com.itheima.demo01Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
finally代码块
格式:
try {
【A】可能会出现异常的代码
} catch(定义一个异常相关的变量) {
【B】出现异常后执行的代码(异常的处理逻辑)
} finally {
【C】一定会执行的代码(释放资源)
}
finally代码块的特点:
finally代码块的内容无论程序是否出现异常都会执行。
*/
public class Demo08finally {
public static void main(String[] args) throws ParseException {
/*
程序执行流程:
1.没有异常: A C D
2.有异常: B C D
*/
/*try {
//【A】可能会出现异常的代码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2022-324");
System.out.println("[A]:"+date);
} catch (ParseException e) {
//【B】出现异常后执行的代码(异常的处理逻辑)
System.out.println("[B]:"+e.toString());
} finally {
//【C】一定会执行的代码(释放资源)
System.out.println("[C]:无论程序是否出现异常,都会执行");
}*/
/*
程序执行流程:
1.没有异常: A C D
2.有异常: C JVM终止程序
*/
try {
//【A】可能会出现异常的代码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2022-324");
System.out.println("[A]:"+date);
} finally {
//【C】一定会执行的代码(释放资源)
System.out.println("[C]:无论程序是否出现异常,都会执行");
}
System.out.println("[D]:后续代码!");
}
}
10.异常处理的注意事项(了解)
1).运行时(期)异常:运行程序之后出现的异常
异常处理的注意事项:
1.运行时(期)异常被抛出可以不处理。
即不捕获(不使用try…catch来捕获处理异常)
也不声明抛出(不使用throws声明抛出异常对象)。
运行时异常,我们无需处理,默认会交给JVM处理==>中断
运行时异常,我们处理没有意义,处理了也是为了让后续代码能继续执行
但是运行时异常本身还是存在的:使用数组索引超出了数组索引的范围,
就算我们使用try…catch处理这个异常实际上数组还是越界的,
没有从根本上解决问题这时候我们应该修改代码,不让数组越界
package com.itheima.demo02Exception;
/*
运行时(期)异常:运行程序之后出现的异常
*/
public class Demo01Exception {
public static void main(String[] args) {
/*try {
int[] arr = {10,20,30};
System.out.println(arr[3]);
} catch (Exception e) {
e.printStackTrace();
}*/
//就算我们使用try...catch处理这个异常实际上数组还是越界的
//在工作中:运行期异常我们必须的修改代码,不让运行期异常出现,从根本上解决问题
int[] arr = {10,20,30};
System.out.println(arr[1]);
System.out.println("后续100行代码!");
}
}
2).子父类异常的处理
异常处理的注意事项:
2.子父类异常的处理
a.如果父类的方法抛出了多个异常,
子类重写父类方法时,子类可以抛出和父类方法相同的异常
子类重写父类方法时,抛出父类异常的子类
子类重写父类方法时,可以不抛出异常
b.父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。
此时子类方法中产生异常,只能捕获处理,不能声明抛出
注意:
子类重写父类的方法,父类方法异常什么样,子类和父类一样就可以了,无序考虑异常问题
爹抛儿子也抛
爹没抛儿子也不抛
package com.itheima.demo02Exception;
public class Demo02Fu {
public void show01() throws NullPointerException,IndexOutOfBoundsException{}
public void show02() throws IndexOutOfBoundsException{}
public void show03() throws Exception{}
public void show04(){}
}
package com.itheima.demo02Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo03Zi extends Demo02Fu {
//子类重写父类方法时,子类可以抛出和父类方法相同的异常
@Override
public void show01() throws NullPointerException, IndexOutOfBoundsException { }
/*
子类重写父类方法时,抛出父类异常的子类
ArrayIndexOutOfBoundsException:数组索引越界异常 extends IndexOutOfBoundsException:索引越界异常
StringIndexOutOfBoundsException:数组索引越界异常 extends IndexOutOfBoundsException:索引越界异常
*/
@Override
//public void show02() throws ArrayIndexOutOfBoundsException { }
public void show02() throws StringIndexOutOfBoundsException { }
//子类重写父类方法时,可以不抛出异常
@Override
public void show03() { }
//父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常
//overridden method does not throw 'java.lang.Exception'
/*@Override
public void show04()throws Exception { }*/
//此时子类方法中产生异常,只能捕获处理,不能声明抛出
@Override
public void show04() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2022-324");
System.out.println("[A]:"+date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
11.自定义异常(使用)
1).概述和基本定义格式
自定义异常:
java给我们提供异常类,不够我们使用,就需要自己定义一些异常相关的类
注意:
1.自定义异常的类名,一般都是以Exception结尾,说明这个类就是一个异常相关的类(见名知意)
2.自定义异常类
a.可以继承Exception:自定义异常就是一个编译期异常
使用:如果在方法中抛出了编译期异常,那么我们就必须处理这个异常,有两种处理方式
1).使用throws关键字在方法上声明抛出这个异常对象,让方法的调用者处理,最终抛出给JVM(中断)
2).使用try…catch自己捕获处理这个异常对象
b.也可以继承RuntimeException:自定义异常就是一个运行期异常
使用:如果在方法中抛出了运行期异常,我们无需处理,默认交给JVM处理(中断)
自定义异常的格式:
public class xxxException extends Exception|RuntimeException{
//定义一个空参数的构造方法
public xxxException(){
super();//调用父类的空参数构造方法
}
//定义一个带异常信息的构造方法
//我们查询异常相关的源码发现,java每个异常类都会定义一个带异常信息的构造方法,把异常信息传递给父类处理
public xxxException(String message){
super(message);//调用父类的空参数构造方法
}
}
2).自定义异常的使用
a.自定义异常类是编译异常
package com.itheima.demo03myException;
//自定义异常类是一个编译期异常
public class RegisterException extends Exception{
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
package com.itheima.demo03myException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/*
注册的案例:
1.定义一个集合,存储用户已经注册过的用户名
2.使用Scanner获取用户本次输入的用户名
3.定义一个方法,在方法中判断用户名是否已经被注册
a.遍历集合获取每一个已经注册过的用户名
b.使用用户本次输入的用户名和遍历得到的用户名比较
c.一致,已经注册过了,抛出RegisterException异常,告之用户您输入的用户名已经被注册了
d.循环结束了,还没有相同的用户名,把用户名存储到集合中
e.给用户提示"恭喜您注册成功!"
*/
public class Demo01 {
public static void main(String[] args) throws RegisterException {
//1.定义一个集合,存储用户已经注册过的用户名
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"jack","rose","汤姆","杰瑞");
//2.使用Scanner获取用户本次输入的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要注册的用户名:");
String name = sc.next();
//调用校验的方法
checkName(list,name);
}
//3.定义一个方法,在方法中判断用户名是否已经被注册
public static void checkName(ArrayList<String> list,String name) throws RegisterException {
//a.遍历集合获取每一个已经注册过的用户名
for (String n : list) {
//b.使用用户本次输入的用户名和遍历得到的用户名比较
if(name.equals(n)){
//c.一致,已经注册过了,抛出RegisterException异常,告之用户您输入的用户名已经被注册了
//RegisterException是一个编译期异常,我们抛出异常之后,必须处理异常
throw new RegisterException("您输入的用户名["+name+"]已经被注册了!");
}
}
//d.循环结束了,还没有相同的用户名,把用户名存储到集合中
list.add(name);
System.out.println(list);
//e.给用户提示"恭喜您注册成功!"
System.out.println("恭喜您注册成功!");
}
}
b.自定义异常是运行期异常
package com.itheima.demo04Exception;
//自定义异常类是一个运行期异常
public class RegisterException extends RuntimeException{
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
package com.itheima.demo04Exception;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/*
注册的案例:
1.定义一个集合,存储用户已经注册过的用户名
2.使用Scanner获取用户本次输入的用户名
3.定义一个方法,在方法中判断用户名是否已经被注册
a.遍历集合获取每一个已经注册过的用户名
b.使用用户本次输入的用户名和遍历得到的用户名比较
c.一致,已经注册过了,抛出RegisterException异常,告之用户您输入的用户名已经被注册了
d.循环结束了,还没有相同的用户名,把用户名存储到集合中
e.给用户提示"恭喜您注册成功!"
*/
public class Demo01 {
public static void main(String[] args) {
//1.定义一个集合,存储用户已经注册过的用户名
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"jack","rose","汤姆","杰瑞");
//2.使用Scanner获取用户本次输入的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要注册的用户名:");
String name = sc.next();
//调用校验的方法
checkName(list,name);
}
//3.定义一个方法,在方法中判断用户名是否已经被注册
public static void checkName(ArrayList<String> list,String name) {
//a.遍历集合获取每一个已经注册过的用户名
for (String n : list) {
//b.使用用户本次输入的用户名和遍历得到的用户名比较
if(name.equals(n)){
//c.一致,已经注册过了,抛出RegisterException异常,告之用户您输入的用户名已经被注册了
//RegisterException是一个运行期异常,我们抛出异常之后,无需处理,默认交给JVM处理
throw new RegisterException("您输入的用户名["+name+"]已经被注册了!");
}
}
//d.循环结束了,还没有相同的用户名,把用户名存储到集合中
list.add(name);
System.out.println(list);
//e.给用户提示"恭喜您注册成功!"
System.out.println("恭喜您注册成功!");
}
}
异常总结:
我们以后调用了一个抛出异常的方法:有两种处理异常的方式
1.使用throws关键字,把异常对象抛出给方法的调用者处理,最终抛出给JVM(中断)
2.使用try…catch自己手动处理异常,可以执行异常之后的代码