目录
1.什么是异常
在程序执行过程中发生的不正常行为/非预期行为,称为异常
public class NormalException {
public static void main(String[] args) {
//异常之前的代码
System.out.println(10/2);
System.out.println(10/0);
System.out.println("after exception");
}
}
int[]arr={1,3,5,7,9};
System.out.println(arr.length);
arr=null;
System.out.println(arr[0]);
System.out.println("after exception");
通过以上两个例子我们发现:
1.当异常发生之后,异常之后的代码不再执行
2.产生错误的原因不同,相关抛出的错误名称也不同
Java中一切皆对象,产生异常的原因不同,就会有对应的“异常类”的产生,因此在Java中异常也是类。
2.Java中有哪些异常
说明:
1.Thorwable这个类是Java中异常的最顶层父类,继承自这个类的子类,当发生一些特殊场景时,由JVM来自动产生该类的对象并传递给程序(就是对象)
2.Error及其子类(内部错误):这类异常一旦发生,当前程序是无法解决的,属于JVM的内部错误。栈溢出Error,堆内溢出Error,这类问题一旦产生,程序只有退出。
public class StackError {
public static void main(String[] args) {
//方法的调用相当于一个“栈帧”入栈的过程。
//每当程序调用一次方法,就会产生一个栈帧这个数据结构
//当无限的调用方法时,一定会将“方法栈”沾满
fun();
}
public static void fun(){
int a=10;
fun();
}
}
3.Exception及其子类:这种异常属于程序开发过程中,可以由程序员写代码进行异常处理的异常类型,当异常发生之后,程序可以捕获该异常,使得异常发生之后,正常的代码可以继续执行。
3.异常的处理流程
以王者荣耀的启动加载为例。两种不同的处理方式:
1.EAFP
try{
//可能出现的方法或代码
//尝试运行
登录游戏:
开始匹配:
确认进入:
选择英雄:
载入界面:
}cath(处理游戏登录异常){
//若登录方法出现问题,在这个代码块中处理
}cath(匹配异常){
//在这个代码块中处理匹配异常
}cath(.......){
//.......
}
2.防御式编程
确保某个方法无误之后再进行执行
boolean ret=false;
ret=登录游戏();
if(!ret){
ret=开始匹配();
}
if(!ret){
ret=确认进入();
}
Java中使用方法1。
异常中的五个关键字:try cath finally throws throw
两个手段
异常的捕获与处理:在当前程序中处理该异常 try cath finally
try{
存放可能出现异常的代码
}cath(异常类型 异常对象){
出现该类异常之后,如何处理
}finally{
无论是否产生异常,最后都会执行的代码
}
三种组合:
try……cath
try……finally
try……cath……finally
try……catch……代码块:异常的捕获并处理
出现异常,JVM就会寻找与之相关的catch代码块
捕获异常,只能捕获特定类型的异常对象,当前catch代码块只能处理除0异常
try……catch代码块
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try{
//try代码块存放可能出现问题的代码
System.out.println(10/5);
System.out.println(10/0);
System.out.println("异常之后的代码块");
}catch (ArithmeticException e){
System.out.println("除0异常");
}
System.out.println("try……catch之后的代码块");
}
}
1.若try中出现的异常,有与之相关的catch代码块捕获,程序处理异常之后,恢复执行直到结束。
2.若catch捕获的异常类型与try中产生的异常类型不相同时,程序还是会退出。
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try{
//try代码块存放可能出现问题的代码
System.out.println(10/5);
int[]arr=null;
System.out.println(arr.length);
System.out.println(10/0);
System.out.println("异常之后的代码块");
}catch (ArithmeticException e){
System.out.println("除0异常");
}
System.out.println("try……catch之后的代码块");
}
}
当try中产生的异常有多种类型时,可以使用多个catch代码块进行捕获处理。
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try{
//try代码块存放可能出现问题的代码
System.out.println(10/5);
int[]arr=null;
System.out.println(arr.length);
System.out.println(10/0);
System.out.println("异常之后的代码块");
}catch (ArithmeticException e){
System.out.println("除0异常");
}catch (NullPointerException e){
System.out.println("空指针异常");
}
System.out.println("try……catch之后的代码块");
}
}
关于多个代码块的说明:
由于异常也是类,异常类之间存在继承关系。
当try代码块中出现很多异常时,且编码时也不确定到底会有多少种异常,就可以使用try代码块捕获exception异常(不推荐)
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try {
//try代码块存放可能出现问题的代码
System.out.println(10 / 5);
int[] arr = {1, 3, 5, 7, 9};
System.out.println(arr[5]);
System.out.println(10 / 0);
System.out.println("异常之后的代码块");
}catch (Exception e){
System.out.println("捕获异常");
}
System.out.println("try……catch之后的代码块");
}
}
若先定义的是父类的catch代码块,则子类catch代码块永远不会执行
若出现多个catch代码块时,程序只会进入一个catch代码块执行
多个catch块只会执行其中一个,处理之后,从第一个正常代码开始恢复执行。
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try {
//try代码块存放可能出现问题的代码
System.out.println(10 / 5);
int[] arr = {1, 3, 5, 7, 9};
System.out.println(arr[6]);
int[]arr1=null;
System.out.println(arr[0]);
System.out.println(10 / 0);
System.out.println("异常之后的代码块");
}catch (ArithmeticException e){
System.out.println("除0异常");
}catch (NullPointerException e){
System.out.println("空指针异常");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组越界异常");
}
// }catch (Exception e){
// System.out.println("捕获异常");
// }
// catch (NullPointerException e){
//
// }
System.out.println("try……catch之后的代码块");
}
}
打印异常的错误堆栈
当异常捕获时,可以调用异常类提供的printStackTrace方法来输出异常的具体信息
public class TryAndCatch {
public static void main(String[] args) {
System.out.println(10+3);
try {
//try代码块存放可能出现问题的代码
System.out.println(10 / 5);
int[] arr = {1, 3, 5, 7, 9};
System.out.println(arr[6]);
int[]arr1=null;
System.out.println(arr[0]);
System.out.println(10 / 0);
System.out.println("异常之后的代码块");
}catch (ArithmeticException e){
e.printStackTrace();
System.out.println("除0异常");
}catch (NullPointerException e){
e.printStackTrace();
System.out.println("空指针异常");
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("数组越界异常");
}
// }catch (Exception e){
// System.out.println("捕获异常");
// }
// catch (NullPointerException e){
//
// }
System.out.println("try……catch之后的代码块");
}
}
finally代码块:
无论是否有异常产生,都一定会执行的代码块。
public class TryFinally {
public static void main(String[] args) {
try{
System.out.println(10/5);
System.out.println(10/0);
}finally {
System.out.println("finally中的代码块");//1
}
System.out.println("异常体系之后的代码块");//2
}
}
1位置不管是否有异常且是否处理,finally位置的代码,都一定会执行
2位置的代码,若程序产生异常且未正常处理时,不会执行
关于finally代码块和return语句:
只要finally代码块存在return语句,无论是否有异常产生,finally中的返回值都会覆盖try或者catch中的返回值。因此一般在finally中不要写返回值
public static int fun(){
try {
System.out.println("try中的代码块");
return 1;
}catch (Exception e){
System.out.println("catch中的代码块");
return 2;
}finally {
System.out.println("finally中的代码块");
return 3;
}
}
3.异常的抛出
throws:
用在方法声明,表示当前方法可能产生的异常,且当前方法不处理该异常。异常产生之后,将异常交给调用者处理。
public class ThrowsTest {
public static void main(String[] args) {
fun();
}
public static void fun()throws NullPointerException{
int[]arr={1,3,5,7,9};
System.out.println(arr.length);
arr=null;
System.out.println(arr.length);
}
}
方法的调用链:JVM->main->fun
若方法中产生了多种异常,可以在方法声明处,声明多个异常类型,和多个catch块类似,实际抛出时,只会抛出遇到的第一个类型。
public class ThrowsTest {
public static void main(String[] args) {
fun();
}
public static void fun()throws NullPointerException,ArrayIndexOutOfBoundsException{
int[]arr={1,3,5,7,9};
System.out.println(arr[5]);
System.out.println(arr.length);
arr=null;
System.out.println(arr.length);
}
}
throw关键字
用在方法体内部,人为进行异常对象的抛出,一般这个关键字搭配自定义异常类来使用。
throw关键字由自己程序产生异常
public class ThrowTest {
public static void main(String[] args) {
fun();
}
public static void fun(){
System.out.println("方法开始");
//此处由程序自己产生了一个空指针的异常现象
//当异常对象产生时==此处发生了空指针异常
throw new NullPointerException();
}
}
综上:
1.throws关键字用在方法声明,表示该方法可能产生的异常,且该方法不处理,交给调用者处理。
2.throw关键字用在方法体内部,表示人为进行异常对象的产生,当异常对象产生时,就相当于此处发生了该类型的异常
一般throw搭配自定义的异常来使用。
自定义异常:
虽然JDK提供了各种各样的异常类,但是实际开发过程中会有很多的业务类异常(例如:命名错误,密码错误),因此开发项目时,会根据项目的具体异常来自定义异常类。
要定义异常类,需要继承JDK的异常父类
补充说明:
关于受查异常与非受查异常
图中:
1.红色框及其子类都属于受查异常,指的是程序编译阶段,若产生了受查异常,必须进行异常处理的异常类,称为受查异常。
无论当前是否有异常产生,调用位置必须进行异常处理,要么try……catch,要么继续向调用链抛出该异常。
public class CheckExecption {
public static void main(String[] args) throws CloneNotSupportedException {
Person person=new Person("王源");
Person person1=person.clone();
}
}
class Person implements Cloneable{
private String name;
public Person(String name) {
}
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class CheckExecption {
public static void main(String[] args) {
Person person=new Person("王源");
try {
Person person1=person.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
class Person implements Cloneable{
private String name;
public Person(String name) {
}
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
2.蓝色框以及其子类都属于非受查异常,编译阶段碰到非受异常,可以不进行异常处理,运行时出现问题再解决。
public class NonCheckedEXception {
public static void main(String[] args) {
fun();
}
public static void fun()throws NullPointerException{
System.out.println("hello");
int[]arr=null;
System.out.println(arr.length);
}
}
自定义的异常只需要继承自Exception或RuntimeException即可,若继承Exception,则当前异常类就属于受查异常,若继承RuntimeException,则当前异常类就属于非受查异常。
public class MyException {
private static String userName="王源";
private static String password="1234";
public static void main(String[] args) throws UserNameException {
login("wy","1234");
}
public static boolean login(String userName,String password) throws UserNameException {
if(userName.equals(MyException.userName)){
System.out.println("用户名正确");
}else {
throw new UserNameException("用户名错误");
}
if(password.equals(MyException.password)){
System.out.println("密码正确");
}else {
throw new PassWordException("密码错误");
}
return true;
}
}
class UserNameException extends Exception{
public UserNameException(String msg){
super(msg);
}
}
class PassWordException extends RuntimeException{
public PassWordException(String msg){
super(msg);
}
}