异常
异常(Exception)
1. 概念
异常是程序执行期间发生的事件,该事件中断了程序指令的正常流程。
当方法内发生错误时,该方法创建一个对象并将其交给运行系统。该对象称为异常对象。包含有关的错误信息,包含错误的类型和发生错误时程序的状态。创建异常对象并将其交给运行时系统称为抛出异常
异常是由方法抛出
运行时异常
运行时异常是只有执行程序才可以见检测出来
(运行时异常)示例:
publicclassExample1{publicstaticvoidmain(String[]args){calculate();}publicstaticvoidcalculate(){intresult=1/0;//该行代码不能被执行,因为发生了异常System.out.println(result);}}异常信息: Exception in thread "main" java.lang.ArithmeticException: / by zero at com.Sonnet.example.Example1.calculate(Example1.java:10) at com.Sonnet.example.Example1.main(Example1.java:6) 异常的类型:java.lang.ArithmeticException ---算数异常 异常的信息:/ by zero --- / 0 程序的状态:at com.Sonnet.example.Example1.calculate(Example1.java:10) at com.Sonnet.example.Example1.main(Example1.java:6) 因为发生了异常,终止了程序的正常执行流程,所以不会打印检测异常
检测异常是不运行,编译器就可以报错,会有红色波浪线提示
(检测异常)示例:
//会有红色的波浪线,正确应该是 float f = 1.0f;floatf=1.0;2. 异常体系
Throwabl
publicStringgetMessage();//获取异常发生的原因publicvoidprintStackTrance();//打印异常在栈中的轨迹ErrorError是一种非常严重的错误,程序员不能通过编码解决。ExceptionException表示是异常的意思主要是程序员在编写代码时考虑不周的问题。异常分为运行时异常和检查异常,一旦程序出现这些异常,程序员应该处理这些异常
RuntimeExceptionRuntimeException表示运行时异常,所有在程序运行时检测抛出的异常类型都属于RuntimeException的子类。运行时异常一般来说程序可以自动恢复,不必处理。
检查异常
检查异常是指编译器在编译代码的过程中,发现不正确的代码,抛出的异常
异常处理
1. 如何处理异常
在Java中异常的种类很多,如果每一种异常类型我们都需要记住,这无疑是一件很困难的事情,如果有一套机制来处理异常。那么将减少程序员在开发的耗时。Java就提供了一套异常处理机制来处理异常。Java提供了5个关键字:throw、throws、try、catch、finally
2. throw抛出异常
throw关键字只能在方法内部使用,throw关键字抛出异常表示自身并对异常进行处理
语法:
throw异常对象;//通常与if选择结构配合使用示例:
staticScannersc=newScanner(System.in);publicstaticvoidmain(String[]args){calculate();}publicstaticvoidcalculate(){System.out.println("请输入一个数字");intnumber1=sc.nextInt();System.out.println("请再输入一个数字");intnumber2=sc.nextInt();//如果除数为0,抛出异常if(number2==0){//ArithmeticException算术异常thrownewArithmeticException("在除法运算中,除数不能为0");//这么写也可以// ArithmeticException e = new ArithmeticException("在除法运算中,除数不能为0");// throw e;}intresult=number1/number2;System.out.println(result);}}异常信息: 请输入一个数字 10 请再输入一个数字 0 Exception in thread "main" java.lang.ArithmeticException: 在除法运算中,除数不能为0 at com.Sonnet.example.Example1.calculate(Example1.java:19) at com.Sonnet.example.Example1.main(Example1.java:10) Process finished with exit code 13. throws声明可能抛出的异常类型
throws关键字只能应用在方法或者构造方法的定义对可能抛出的异常类型进行声明,自身不会对异常进行处理,由方法的调用者来处理。如果方法的调用者未处理,则异常持续向上一级抛出,直至main()方法为止,如果main()方法也未处理,那么程序可能因此终止
语法:
访问修饰符 返回值类型 方法名(参数列表)throws异常类型1, 异常类型2,···{}示例:
publicclassExample2{privatestaticScannersc=newScanner(System.in);publicstaticvoidmain(String[]args){intnumber=getNumber();System.out.println(number);}//执行该方法可能会抛出InputMismatchException---输入不匹配异常publicstaticintgetNumber()throwsInputMismatchException{System.out.println("请输入一个数字");intnumber=sc.nextInt();returnnumber;}}throws可以声明方法执行时可能抛出的异常类型,但需要注意的是:方法执行过程中只能抛出一个异常
4. try-catch异常
throw和throws关键字均没有对异常进行处理,这可能导致程序终止。这种情况下,可以使用try-catch结构来对抛出异常进行捕获处理,从而保证程序能够正常运行
语法:
try{//代码块}catch(异常类型 异常对象名){}其中try表示尝试的意思,尝试执行try结构中的代码块,如果允许过程中抛出了异常,则交给catch语句进行操作
示例:
publicclassExample3{privatestaticScannersc=newScanner(System.in);publicstaticvoidmain(String[]args){//尝试执行try中的代码,发生异常将其抓住try{intnumber=sc.nextInt();}catch(InputMismatchExceptione){System.out.println("请不要瞎写,只能输入数字");}System.out.println("发生异常也会执行");}}a 请不要瞎写,只能输入数字 发生异常也会执行即便发生了异常,依旧会继续执行之后的代码
那么如何知道异常出现在哪里?,可以通过
printStackTrance()打印异常轨迹和
getMessage();获取异常信息来确定示例:
publicstaticvoidmain(String[]args){try{intnumber=sc.nextInt();System.out.println(number);}catch(InputMismatchExceptione){//打印异常轨迹e.printStackTrace();System.out.println(e.getClass().getName());System.out.println("异常信息:"+e.getMessage());System.out.println("请不要瞎写,只能输入数字");}System.out.println("发生异常也会执行");}}a java.util.InputMismatchException at java.base/java.util.Scanner.throwFor(Scanner.java:939) at java.base/java.util.Scanner.next(Scanner.java:1594) at java.base/java.util.Scanner.nextInt(Scanner.java:2258) at java.base/java.util.Scanner.nextInt(Scanner.java:2212) at com.Sonnet.example.Example3.main(Example3.java:12) java.util.InputMismatchException 异常信息:null 请不要瞎写,只能输入数字 发生异常也会执行
思考:如果一个方法可能抛出多个异常,如何捕获?
可以在try后面添加多个catch字句来分别对每一种异常进行处理—多分支catch
示例:
publicclassExample3{privatestaticScannersc=newScanner(System.in);publicstaticvoidmain(String[]args){intnum=divide();System.out.println(num);}publicstaticintdivide()throwsInputMismatchException,ArithmeticException{try{intnumber1=getNumber();intnumber2=getNumber();returnnumber1/number2;}catch(InputMismatchExceptione){System.out.println("请不要乱输入,只能输入数字");}catch(ArithmeticExceptione){System.out.println("除法运算中0不能当除数");}return0;}publicstaticintgetNumber()throwsInputMismatchException{System.out.println("请输入一个数字");intnumber=sc.nextInt();returnnumber;}}请输入一个数字 a 请不要乱输入,只能输入数字 0请输入一个数字 1 请输入一个数字 0 除法运算中0不能当除数 0throw 抛出, throws 声明, try-catch处理
5. finally语句
finally语句不能单独存在,必须与try语句或者try-catch结构配合使用,表示无论程序是否发生异常都会执行,主要用于释放资源。但如果try语句或者catch语句存在系统退出的代码,则finally语句将得不到执行。
System.exit(0);//0: 系统正常退出System.exit(1);//非0: 系统非正常退出语法:
try{}finally{}//或者try{}catch(异常类型 异常对象名){}finally{}示例一:
publicclassExample4{privatestaticint[]numbers={1,2,3,4,5};publicstaticvoidmain(String[]args){try{//数组越界intnumber=getNumberArray(10);System.out.println(number);}finally{System.out.println("需要执行的代码");}}publicstaticintgetNumberArray(intindex){returnnumbers[index];}}需要执行的代码 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 at com.Sonnet.example.Example4.getNumberArray(Example4.java:17) at com.Sonnet.example.Example4.main(Example4.java:9)示例二:系统退出
publicclassExample4{privatestaticint[]numbers={1,2,3,4,5};publicstaticvoidmain(String[]args){try{//系统正常退出,如果非0,那么是异常退出System.exit(0);//数组越界intnumber=getNumberArray(10);System.out.println(number);}finally{System.out.println("需要执行的代码");}}publicstaticintgetNumberArray(intindex){returnnumbers[index];}}什么都没有输出,因为退出了系统,但是改变
System.exit(0);会有不同效果
示例三:先抛出异常,在系统退出
publicclassExample4{privatestaticint[]numbers={1,2,3,4,5};publicstaticvoidmain(String[]args){try{//先抛出异常在退出//数组越界intnumber=getNumberArray(10);System.out.println(number);//系统正常退出,如果非0,那么是异常退出System.exit(0);}finally{System.out.println("需要执行的代码");}}publicstaticintgetNumberArray(intindex){returnnumbers[index];}}需要执行的代码 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 5 at com.Sonnet.example.Example4.getNumberArray(Example4.java:20) at com.Sonnet.example.Example4.main(Example4.java:10)发现 需要执行的代码 被输出了,说明并没有退出系统
因为在抛出了异常之后,就开始执行finally语句了
sout(number)和退出系统并没有执行
示例四:
publicclassExample4{privatestaticint[]numbers={1,2,3,4,5};publicstaticvoidmain(String[]args){try{//数组越界intnumber=getNumberArray(10);System.out.println(number);//系统正常退出,如果非0,那么是异常退出System.exit(0);}catch(ArrayIndexOutOfBoundsExceptione){System.out.println("数组下标越界了");}finally{System.out.println("需要执行的代码");}}publicstaticintgetNumberArray(intindex){returnnumbers[index];}}数组下标越界了 需要执行的代码如果在catch语句中添加,系统退出,那么finally语句就不会执行了
面试题:
分析如下代码的执行结构:
publicclassExample5{publicstaticvoidmain(String[]args){intresult=getResult();System.out.println(result);}publicstaticintgetResult(){intnumber=10;try{returnnumber;}catch(Exceptione){return1;}finally{number++;}}}运行结果为10
try是尝试执行,尝试返回
所以尝试返回一个结果,但发现后面还有一个finally模块,而finally模块一定会执行。
于是在这里只能将返回值用一个临时变量(例如变量a)存储起来。
然后再执行finally模块,再将这个临时变量(a)存储起来的值返回。
但number++,是将number + 1,和临时变量(a)没有关系。
如果finally模块是 return ++number;
那么得到的result就是11
自定义异常
1. 为什么要使用自定义异常
再Java中,异常的类型非常多,要想使用这些异常,首先必须熟悉它们。这无疑是一个巨大的工作,很耗费时间。如果我们可以自定义异常,则只需要熟悉RuntimeExceptiom、Exception、Throwable即可。这大大缩小了范围,自定义异常还可以帮助我们快速定位问题。
自定义运行时异常语法:
publicclass类名extendsRuntimeException{}自定义检查异常语法:
publicclass类名extendsException{}示例:
在登录时经常会看到:用户名提示不存在或者账号密码错误。请用自定义异常来描述
publicclassExample6{privatestaticScannersc=newScanner(System.in);publicstaticvoidmain(String[]args){System.out.println("请输入账号");Stringusername=sc.next();System.out.println("请输入密码");Stringpassword=sc.next();//检查异常,必须要做处理try{login(username,password);}catch(UsernameNoFoundExceptione){e.printStackTrace();}catch(BadCredentialExceptione){e.printStackTrace();}}publicstaticvoidlogin(Stringusername,Stringpassword)throwsUsernameNoFoundException,BadCredentialException{if("admin".equals(username)){if("123456".equals(password)){System.out.println("登录成功");}else{thrownewBadCredentialException("账号或密码错误");}}else{thrownewUsernameNoFoundException("账号不存在");}}}请输入账号 admin 请输入密码 123456 登录成功请输入账号 jiayi 请输入密码 123 com.Sonnet.example.UsernameNoFoundException: 账号不存在 at com.Sonnet.example.Example6.login(Example6.java:33) at com.Sonnet.example.Example6.main(Example6.java:17)请输入账号 admin 请输入密码 123 com.Sonnet.example.BadCredentialException: 账号或密码错误 at com.Sonnet.example.Example6.login(Example6.java:30) at com.Sonnet.example.Example6.main(Example6.java:17)2. 异常使用注意事项(重要)
运行时异常可以不做处理
publicclassExample7{publicstaticvoidmain(String[]args){int[]arr={1,2,3,4,5,6};//并没有报错System.out.println(arr[6]);}}如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是该类的子集。(与协变返回类型原理一致)
示例:
publicclassfather{//父类声明抛出了异常publicvoideat()throwsException{}}publicclasschildextendsfather{//必须要重写方法,并且同的异常或者是该类的子集//可以抛出Exception或者RuntimeException等//但不能抛出父类Throwable@Overridepublicvoideat()throwsException{super.eat();}}
父类方法没有抛出子集,子类覆盖父类该方法时也不可抛出检查异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
示例:
publicclassfather{publicvoideat()throwsException{}//父类没有声明抛出异常publicvoidsleep(){}publicvoidlogin(){}}publicclasschildextendsfather{Stringusername;Stringpassword;publicchild(Stringusername,Stringpassword){this.username=username;this.password=password;}//必须要重写方法,并且同的异常或者是该类的子集//可以抛出Exception或者RuntimeException等//但不能抛出父类Throwable@Overridepublicvoideat()throwsException{super.eat();}//父类没有声明抛出异常//此时子类只能声明抛出运行时异常//不能抛出检查异常@Overridepublicvoidsleep()throwsRuntimeException{}//父类中的方法没有声明抛出异常,子类中方法不能声明抛出检查异常//只能捕获处理@Overridepublicvoidlogin(){try{Example6.login(username,password);}catch(UsernameNoFoundExceptione){thrownewRuntimeException(e);}catch(BadCredentialExceptione){thrownewRuntimeException(e);}}}
要重写方法,并且同的异常或者是该类的子集
//可以抛出Exception或者RuntimeException等
//但不能抛出父类Throwable
@Override
public void eat() throws Exception {
super.eat();
}
//父类没有声明抛出异常 //此时子类只能声明抛出运行时异常 //不能抛出检查异常 @Override public void sleep() throws RuntimeException{ } //父类中的方法没有声明抛出异常,子类中方法不能声明抛出检查异常 //只能捕获处理 @Override public void login() { try { Example6.login(username, password); } catch (UsernameNoFoundException e) { throw new RuntimeException(e); } catch (BadCredentialException e) { throw new RuntimeException(e); } } } ```