每周总结14 - java异常
一、异常的概念
异常,指非开发过程中的语法和逻辑错误所导致的意外失败错误等情况。
非程序设计问题,
所导致的意外错误情况。
例如:文件找不到、网络链接失败、非法参数(用户输入错误,使用错误)等原因导致的程序无法正常运行问题。
二、异常类的介绍
java通过API 中的 Throwable类的众多子类来描述各种不同的异常。
所以Java异常皆是Throwable子类的实例化对象。
Throwable有两个重要的子类:
Exception (异常) 和 Error (错误)。
二者皆是java异常处理的重要子类,各自又包含大量子类。
1.Error
从技术上讲,Error及其子类对象不是异常,他们代表了程序运行时java系统内部的错误,与程序设计者的操作无关。
Error是不可查的。
因为其在应用程序的控制和处理能力之外,绝大多数是程序运行时不允许出现的情况。
程序员无能为力,因此不必处理。
比如OutOfMemoryError,JVM内存资源(空间资源)不足以运行操作,将会产生此异常。
(其实应该是自己终止,然后将这个被如此合理命名的实现存储的错误种类发送显示给你进行反馈通知。
即是Error子类的一种异常类起到了他的作用。实际上没有这些异常类应该也会出现错误结果或者可怕后果。
要么JVM自己终止了,要么异常类起了调节作用(or JVM检测到异常(类)发生即刻就终止运行了)
)
这种由于物理等客观因素引起的错误无法避免,因此仅仅设立对应Error类进行反馈通知即可。
(C好像也有类似的底层设置,应该通过手段编写了。)
2.Exception
Exception通常是因为某个资源不可用,或者正确执行程序所需要的条件不满足造成的。
是程序本身可以处理的异常,程序员应该尽可能地加以处理的部分。
java将Exception分为两类:
运行时异常(RuntimeException,也称未检查异常) 和 非运行时异常(也称为已检查异常)
(1) 运行时异常
包括Java.lang.RuntimeException类以及他的所有子类。
即是非受控异常,未检查异常。
(2) 非运行时异常
除此之外,所有属于Exception及其子类的异常都属于非运行时异常。
即是受控异常,可检查异常。
运行时异常(RuntimeException以及他的子类)和错误(Error)都属于不可查异常,编译器不会强制要求处置。
而对于非运行时异常,编译器会强制要求进行处理。
非运行时异常,即是编译时异常,即是受控异常
throwable可抛出的。
出现错误Error,程序大多就直接死了。
一般发生这种异常,JVM会选择终止程序。
注意两个概念:
写代码的时候,编译时报错的异常,就是编译时异常
写代码的时候,编译时没有报错的异常,就是运行时异常(RuntimeException)
三、编译时异常如何处理?
有两种方式
1.上抛
throws. and throw
java异常体系中的异常都是在运行时由系统抛出的。
用户自定义的异常必须自行抛出。
throw 抛出用户自定义的异常。
throw 用于自行抛出异常,抛出的不是异常类而是异常对象,每次只能抛出一个异常对象,
格式:
throw 异常对象;
一旦执行了throw语句,以后的代码都不会被执行。
方法抛出异常,在()和{之间throws,无论是不是系统or自定义,手动抛出异常,则一定是自定义,一定是throw,一定是异常对象
我没有能力去处理它,那就只能上抛,交给调用我的方法去处理。
就像下面的人没有办法处理问题就上抛(反馈问题)给上级,交由上级处理问题。
要么问题被捕获处理
要么一直上抛到main()方法任然没有被处理时
main()方法就会上抛给JVM。
JVM就会终止程序的运行。
throws 出现在方法头中 ()之后,{之前
例如
public void show(int a) throws IOException{方法体}
所以最好将异常在到达main()方法前处理。
不要抛给JVM让他自动处理,就中断了。
java中规定,当子类重写父类方法的时候。
子类方法抛出的异常类型不能比父类的更大更款宽泛。(合理,)
只能是相同类型or其子类。
当然,也可以补抛出异常。
2.捕获
完整的异常捕获和处理代码 由 try-catch-finally 语句实现。
编译时异常(即,可查,受控异常)
一定要处理,不然编译器不给过。这也是可以处理的异常
try{
//你所要运行但是可能会抛出异常的代码块正文
}catch(异常类型1 变量名){
//此处类似于形参,此处变量名是你随便取的,用于标记这个产生的异常对象。
}catch(异常类型2 变量名){
//然后一致重复此步骤,直到涵盖你觉得可能出现的所有异常。
}catch(3 e{
e.printStackTrace();
//catch示例,printStackTrace()是一个重要的方法,可以打印异常的详细信息。
//打印出来的信息叫做 堆栈追踪信息
//他告诉开发人员,问题出在哪里。
//一般都用这个方法,这个方法出现在catch语句块中。
}finally{
//此处为最后一定会执行的代码块(当然,也有可能出异常,如果有可能抛异常,也要用try语句。
//没错这里可以try语句实现嵌套,不干扰。)
//又来一套try catch finally.
}
格式如上。
一般有try就会有catch,不然没有意义。
finally是尾处理操作,有时候可以不写。
如果finally中野可能抛异常,则也需要用try,重要
一旦try中语句出现异常。
将立刻终止语句,开始对比捕获。问题语句的下面语句皆不会执行。
然后开始匹配。
直到匹配到对应的异常类型。(即,catch中的异常类型是其父类型Or对应相同类型 )
异常类型应该根据需求写,一般锁定到最精确的那个类,最细化分类,只想笼统的搞一下就可以用大的异常类
即刻执行对应的catch语句。
然后跳到finally语句执行。
到此try-catch-finally语句
执行完毕。
如果try语句没有出现异常。
则会跳过所有catch语句去执行finally语句。
也就是说finally语句一定会被执行。
然后继续阅读其后的代码。
tips:
catch等语句,java中的{}
是一个作用域。c语言也是吧。
其中定义的变量是局部变量。
四、自定义异常(手动抛异常)
根据老师说是最重要的部分。
先示例
public class MyException extends Exception{
String Message;//成员属性
public MyException(){}
public MyException(String message){
super(message);
}
}
固定写法
成员属性String(这个好像可以省略)
至少定义两个构造方法。
一个是无参
一个是有String参数的有参构造。
将此对象传递给父类Exception的相同构造方法。//所以第一行要写super()用父类构造方法去返回详细信息吧
这个对象将作为该异常对象的详细描述信息。
即是getMessage()方法的返回值。
extends Exception 而不是RuntimeException或者Error。
写的是可以处理的编译时异常。
并不是所有异常都要处理,人和系统都会崩溃的,所以有些会忽略喽(除了终止以外)
自定义异常和手动抛异常示例
package com.XGX.Test.myThread;
import com.XGX.Test.myException.*;
public class myThread {
public static void main(String[] args){
try {
throw new myException();
}catch(myException e){
e.printStackTrace();
}finally{}
}
public static void show()throws myException{
System.out.println("1");
}
}
---
package com.XGX.Test.myException;
public class myException extends Exception{
//自定义异常,给特定名称继承Exception类or RuntimeException类。
//static final int serialVersionUID 3;//这样不行,纯粹没学好,静态定义格式都写错了,忘记写 = 符号了。
static final int serialVersionUID = 314;
public myException(){}
public myException(String msg){
super(msg);//无参和有参。有参给一个String,然后第一行写一个super(),把那个字符串传进去。
}
//定义的两个构造方法
//super()是给父类传值的,Exception和RuntimeException
}
//运行结果
"C:\Program Files\Java\jdk1.8.0_321\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar=57259:D:\IntelliJ IDEA 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_321\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_321\jre\lib\rt.jar;D:\myProject\Test\out\production\Test" com.XGX.Test.myThread.myThread
com.XGX.Test.myException.myException
at com.XGX.Test.myThread.myThread.main(myThread.java:8)
Process finished with exit code 0
这个示例是直接强行抛出。。。直接throw,连show()都没用了
应该是这样用(当然强行抛出也行,但是没太大意义(抛出自定义异常(对象)用throw就对了,但是方法声明部分还是要用throws写,声明抛出异常)
//修改代码用带String参数的构造方法。throw new myException("hello");
package com.XGX.Test.myThread;
import com.XGX.Test.myException.*;
public class myThread {
public static void main(String[] args){
try {
myThread.show();//静态方法用类名直接调用
}catch(myException e){
e.printStackTrace();//打印详细信息。
}finally{}
}
public static void show()throws myException{
System.out.println("1");
throw new myException("hello");
}
}
//运行结果:(省略了结果上面的一部分)
1
com.XGX.Test.myException.myException: hello//<-出现了我们的信息提示,进行了反馈。
at com.XGX.Test.myThread.myThread.show(myThread.java:15)
at com.XGX.Test.myThread.myThread.main(myThread.java:8)
so
throw的用法
定义方法,throws你自定义的异常(预示,告知main(),JVM,你这个方法是要抛异常的,也就是声明
然后在方法体中手动抛出,因为不是系统自带,不会自动上抛,就要你手动抛出
这样才能捕获处理。
tips:
在示例中可以发现。
因为定义的是编译时异常,必须要处理。
所以只能吧throw语句写在try语句中,用catch去进行捕获。(even甚至你没捕获到都会报错不给你过)
2.finally必须和try联合使用
然后finally语句一定会执行,其优先级甚至高过了retrun.合理奥。(如果return语句在try里面)
3.catch捕获的异常
先捕获小的,再捕获大的。
即如果捕获的异常之间有父类子类的关系。
先捕获子类,再尝试捕获父类。
这样详细一点。
不会出现截胡的现象。
catch捕获的异常是先小后大
五、阅读控制台输出的异常信息
1.找到对应的行数就行
2.看结果最上面的那条堆栈追踪信息。
六、课后习题9
package com.XGX.Test.myThread;
public class myThread {
public static void main(String[] args){
try{
newExce();
System.out.println("1");
}catch(Exception e){
System.out.println("2");
}finally{
System.out.println("3");
}
System.out.println("4");
}
public static void newExce() throws Exception{
throw new Exception();
}
}
//
结果是
2
3
4
---
public class myThread {
public static void main(String[] args) {
int test = test(3,5);
System.out.println(test);
}
public static int test(int x,int y){
int result = x;
try{
if(x<0 || y<0){
return 0;//有小于0就返回0.return了,后面的代码就不会看了
}
result = x + y;
return result;
}finally{
System.out.println("result ="+ (x - y));
}
}
}
//结果:
result = -2
8
//逆天,这里先是3-5.然后才是3+5;
//这里说明了什么? 说明了 finally的优先级比return还要大。至少是比try语句中的return 要大。
package com.XGX.Test.myThread;
public class myThread {
public static void main(String[] args) {
try{
func();//我草,虽然他没有throws异常,但是他手动throw了一个
}catch(Exception e){
System.out.println("C");
}
System.out.println("D");
}
public static void func(){
try{
throw new Exception();
//System.out.println("A");//编译过不了,无法访问
}catch(Exception e){
System.out.println("B");
}
}
}
不能通过编译,throw后的语句无法执行。
6,本来应该可以过的,这是为了防止你这个小可爱不知道哪里出错了,直接给你编译不给过。
免得你到处看哪里有问题导致结果和预料中不一样。
我真是爱了爱了(给心心)
---
package com.XGX.Test.myThread;
//static class Exc0 extends Exception{};静态类只能为静态内部类,作为属性,不然在外面编译都过不了。
public class myThread {
static class Exc0 extends Exception{}
static class Exc1 extends Exc0{}
public static void main(String[] args) {
try{
throw new Exc1();
}catch(Exception e){
System.out.println("Exception");
}catch(Exc0 e){
System.out.println("Exc0");
}
}
}
//编译不给过,因为Exc0是Exception的子类,catch所捕获的异常应该先小后大,先细后粗。
//这才是合理的捕获顺序,一级一级去捕获,不会造成捕获了相对大而粗犷的异常,导致信息不够详细的情况。