结合学习和菜鸟笔记整理~附菜鸟教程中关于异常的网址
https://www.runoob.com/java/java-exceptions.html
我们为什么要处理异常?当程序执行过程中,遇到某一个异常(该异常通常难以解决,例如拔网线产生的断网异常)将会直接中止执行,这对用户的体验是非常不好的。为了保证之后的程序能够正常顺利的执行,我们引入面向对象异常机制。
遇到错误系统抛出异常,我们捕获异常,处理输出错误提示,继续执行程序其他部分
异常的分类
- 检测性异常:可检测性异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器就不通过,不允许编译。编译器要检查这类异常,检查的目的一方面是因为该类异常的发生难以避免,另一方面就是让开发者去解决掉这类异常,所以称为必须处理(try …catch / throws)的异常。
如果不处理这类异常,集成开发环境中的编译器一般会给出错误提示。
例如:FileInputStream fis=new FileInputStream("./src/io/BRDemo.java");
该语句必须try…catch或者throws 来规避IOException
- 非检测性异常/运行时异常:简单说是都属于可以通过逻辑来纠正的错误,可以称之为bug。类似于空指针异常,我们完全可以通过if分支判断来避免这类异常,亦或那种十分常见的语句。
如果不处理这类异常,集成开发环境中的编译器也不会给出错误提示。
例如:空指针异常NullPointerException即使我们有,系统也不会提示我们必须捕获,可以进行修改程序进行处理,避免。
只有RuntimeException类属于非检测异常,因为普通JVM在操作时候引起的异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在java应用程序中会频繁出现,因为他们不受编译器检查与处理或者声明规则的限制常见的RuntimeException的子类型
- **错误:**错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception的层次关系
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
捕获异常:try—catch—finally
基础语法
try{
可能出现异常的代码片段
}catch(XXXException e){
当try中出现XXXException后的解决办法
}finally{
无论try中是否发生异常,finally中代码始终执行
可以存放清理类型等收尾善后性质的语句
比如IO操作后的close()调用。
}
//举例:
public class Demo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {
String str = null;
System.out.println(str.length());
return;//方法实际返回前也要先执行完finally
}catch(Exception e){
System.out.println("出错了!");
}finally {
System.out.println("finally中的代码执行了!");
}
System.out.println("程序结束了");
}
}
扩展:
-
多重捕获块:一个 try 代码块后面跟随多个 catch 代码块。
注意 :多重捕获一定是子类型的异常在上,超类型/大范围的异常在下
try{
}catch(){
}catch(){
}catch(){
}...
- 可以合并捕获异常,当不同异常处理手段相同时,可以用这种方式。
try{
需要检查的语句块
}catch(NullPointerException|StringIndexOutOfBoundsException e){
System.out.println("出现了空指针或下标越界的处理!");
}catch(Exception e){
System.out.println("反正就是出了个错!");
}
注意:
-
try语句块不能独立存在,后面必须跟catch或finally。
-
try, catch, finally 块之间不能添加任何代码。
IO中的异常处理:
我们先来看以下代码
public class FinallyDemo2 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.dat");
fos.write(1);
}catch(IOException e){
System.out.println("出错了!在这里解决了!");
}finally{
try {
if(fos!=null) { //防止空指针异常
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我们可以发现十分的麻烦,因为finally中的也要进行捕获。JDK1.7引入了自动关闭特性,使得我们在源代码中异常处理机制在IO应用中得到了简化:AutoCloseable
需要注意,该特性依然是编译器认可的,而非虚拟机认可。只是编译器帮我们做了上面麻烦的事情
AutoCloseable是一个接口,实现该接口的类将具有自动关闭的特性。
具体我们如何去实现呢?
public class AutoCloseableDemo {
public static void main(String[] args) {
//在try后面有个括号(),里面装相关的语句
try(
/*
只有实现了AutoCloseable接口的类才可以在这里定义并初始化。
编译器在编译时会将在这里定义的变量在finally中调用close将其关闭。
最终编译器会将当前代码改为FinallyDemo2的样子。
*/
FileOutputStream fos = new FileOutputStream("fos.dat");
){
String line = "hello";
fos.write(1);
}catch(IOException e){
System.out.println("出错了");
}
}
}
throw和throws
throw
引入:Java处理异常的方式,在Java代码中如果发生异常的话,jvm会抛出异常对象,导致程序代码中断,这个时候jvm在做的操作就是:创建异常对象,然后抛出:这是jvm自动帮我们操作的,如下:
int i=1; int j=0; int res=0;
res=i/j; //发生除零错误,系统运行到这里的时候会中断并自动抛出异常
但是!对于一些特殊的应用需求,比方用户程序自定义的异常和应用程序特定的异常就需要借助于throw语句来主动抛出,举例:
public void setAge(){
int age = 0 ;
age = -100 ;
if(age<0 )
{
throw new Exception("IllegalAgeException");//创建异常对象
}
System.out.println(age);
}
这里当调用setAge方法的时候,系统就会知道这里可能会产生异常,需要处理。他要求我们必须进行try…catch或者throws
throws
当该方法/语句throw了一个方法时,我们需要进行处理。如果不想亲自try…catch此异常。我们可以在方法声明后抛出异常类名。这样当别人通过类调用该方法的时候,就知晓我需要处理这种异常(当然我可以继续抛出,知道main方法)
语法注意点:throws表示抛出异常,由该方法的调用者来处理;可以跟多个异常类名,用逗号隔开;throws表示出现异常的一种可能性,并不一定会发生这些异常
举例:
void fun()throws IOException,SQLException {
xxx;
} //表示如果执行fun()方法时候,可能会产生IO,SQL异常,并且fun方法内部并没有进行处理
main(){//main方法调用,就需要进行处理该两个异常
try{
fun();
}catch(IOException){
}catch(SQLException){
}
}
小结
throw:则是用来抛出一个具体的异常类型。用在方法体内,跟的是异常对象名,只能抛出一个异常对象名。表示抛出异常,由方法体内的语句处理,throw则是抛出了异常,执行throw则一定抛出了某种异常。
throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。用在方法声明后面,跟的是异常类名。可以跟多个异常类名,用逗号隔开throws表示出现异常的一种可能性,并不一定会发生这些异常。
子类重写父类方法时throw的规则
配合例子来解释:
我们令父类如下:
public class ThrowsDemo {
//dosome方法抛出两个异常
public void dosome()throws IOException, AWTException {}
}
子类重写父类的方法
-
以下写法可以
class SubClass extends ThrowsDemo{ //抛出相等的异常 public void dosome()throws IOException, AWTException {} //允许仅抛出部分异常 public void dosome()throws IOException{} //允许不再抛出任何异常 public void dosome(){} //允许抛出超类方法抛出异常的子类型异常 public void dosome()throws FileNotFoundException {} }
-
以下写法是不允许的
class SubClass extends ThrowsDemo{ //不允许抛出额外异常(超类方法没有声明抛出的,或和超类声明抛出的异常没有继承关系的) public void dosome()throws SQLException {} //不允许抛出超类方法抛出异常的超类型异常 public void dosome()throws Exception {} }
Java常见异常–篇幅过长单独写一篇~