什么是异常

异常是指程序运行可能出现的不能正常继续的情况,也可以理解为程序出现了不在预期范围内的一些情况,都可以称之为异常。

异常的分类

  所有的异常类是从java.lang.Exception类继承的子类。Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error 。Java程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。Error用来指示运行时环境发生的错误。例如,JVM内存溢出。一般地,程序不会从错误中恢复。层次关系图如下

          Throwable

    Error            Exception

            other Exception      RuntimeException

  所以从上图可以看出,异常分为两类:

  1. 运行时异常:运行的时候才出现的,程序编译期间无法预知,这种情况一般不做捕获。出现运行期异常一般是代码不严谨,需要修改代码。例如下面的例子

    /**
    * Created by lili on 15/11/25.
    */
    public class ExceptionTest {
    public static void main(String[] args) {
    int a = 10;
    int b = 0;
    System.out.println(a/b);
    System.out.println("---------");
    }
    }

    上述代码就是代码不严谨所致,分母不能为0,所以程序运行至a/b会抛出异常Exception in thread "main" java.lang.ArithmeticException: / by zero

    java.lang.Object
    继承者 java.lang.Throwable
    继承者 java.lang.Exception
    继承者 java.lang.RuntimeException
    继承者 java.lang.ArithmeticException

    查看该异常,发现是运行期异常,所以不能捕获,需要给出处理,例如加一个if判断。

  2. 非运行时异常:在编译期间就能发现的异常,必须要明确捕获或者抛出,不然程序编译无法通过。例如下面的代码:
    /**
    * Created by lili on 15/11/25.
    */
    public class ExceptionTest {
    public static void main(String[] args) {
    String dateStr = "2015-11-22";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(dateStr); }
    }

    上述代码无法通过编译,因为sdf.parse(dateStr)可能出现问题(尽管没问题),所以要做异常处理。

    public class ExceptionTest {
    public static void main(String[] args) {
    String dateStr = "2015-11-22";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    try {
    Date d = sdf.parse(dateStr);
    } catch (ParseException e) {
    e.printStackTrace();
    } finally {
    System.out.println("异常处理后");
    }
    System.out.println("结束在这里");
    }
    }

处理异常的几种方法

  1. try{语句;}catch(异常名 e){异常处理}catch(异常名 e){异常处理}...finally{结束处理}
    上述是异常正面处理的方式,对可能出现异常的语句进行try catch,try包含的语句的原则是尽量要少,try catch可以有多层,例如上面的对日期处理的异常处理可以写成下面这样的多层异常处理,最下面一层处理所有可能出现的异常。

            try {
    Date d = sdf.parse(dateStr);
    } catch (ParseException e) {
    e.printStackTrace();
    System.out.println("异常处理1");
    } catch(Exception e){
    e.printStackTrace();
    System.out.println("异常处理2");
    }finally {
    System.out.println("异常处理后");
    }

      也就是说,如果抛出的是异常A,我们处理异常A的父亲异常是可以的。但是,一般建议catch准确的异常。上述多层异常处理的前后关系必须是父亲异常必须在后,即异常范围越大越靠后放,即最后才可能去比较,这也符合精确处理的原则,因为如果将Exception写在最前面,后面的处理都不会执行。最后不管是否有异常需要处理,finally都会执行。
      JDK7提供了一种新的异常捕获方式,该方法用户捕获同等级的异常,并且这些异常的处理方法可以有相同的处理方式。

            try{
    
            }catch (ArithmeticException | ArrayIndexOutOfBoundsException e){
    System.out.println("同类异常处理");
    }finally { }

    try catch执行的逻辑:

    在try里面发现问题后,jvm会帮我们生成一个异常对象,然后把这个对象抛出,和catch里面的类进行匹配。
    一旦有匹配的,就执行catch里面的处理,然后结束了try...catch 继续执行后面的语句。

  2. throws:当出现的异常我们没有能力或者权限去处理时,可以在改方法体的()后面去抛出。
    public class ExceptionTest {
    public static void main(String[] args) {
    String dateStr = "2015-11-22"; try {
    dateFormat(dateStr);
    } catch
    (ParseException e) {
    e.printStackTrace();
    }
    System.out.println("这行代码可以执行");
      

    } public static void dateFormat(String str) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       Date d = sdf.parse(str);
    }
    }

    上述代码展示了如何使用throws,但是调用该方法的地方还是要面临该异常。面临的时候可以选择处理或者继续抛出,但是在main方法里最好不要再抛出,不然就抛给JVM。抛给JVM的话,一旦出现问题,那后面的代码就无法继续执行了。

    public class ExceptionTest {
    public static void main(String[] args) throws ParseException {
    String dateStr = "2015-11-22";
    dateFormat(dateStr);
    System.out.println("这行代码执行不了"); } public static void dateFormat(String str) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date d = sdf.parse(str);
    }
    }
  3. throw:如果出现了异常情况,我们可以把该异常抛出,这个时候的抛出的应该是异常的对象。
    public class ExceptionTest {
    public static void main(String[] args) {
    int a = 10;
    int b = 0;
    division(a,b);
    } public static void division(int a,int b){
    if(b==0){
    throw new ArithmeticException("被除数为0");
    }
    System.out.println(a/b);
    System.out.println("---------");
    }
    }

    上面之所以不会提示处理division,是因为ArithmeticException是运行时异常的子类,所以这是程序有问题,要改善程序。

throws和throw的区别

  1. throws

   用在方法声明后面,跟的是异常类名
   可以跟多个异常类名,用逗号隔开   
     表示抛出异常,由该方法的调用者来处理
     throws表示出现异常的一种可能性,并不一定会发生这些异常


  2. throw

用在方法体内,跟的是异常对象名
  只能抛出一个异常对象名
  表示抛出异常,由方法体内的语句处理
  throw则是抛出了异常,执行throw则一定抛出了某种异常

finally关键字:如果执行finally之前JVM意外退出,finally也有可能执行不了

  finally一般用在try catch中用来做最后的一些收尾工作,如释放资源,在IO操作和数据库操作中用的比较多。但是如果在catch中有return语句,finally是否会执行呢?

public class ExceptionTest {
public static void main(String[] args) {
int a = 10;
System.out.println("return a:"+ test(a));
} public static int test(int a) {
try{
a = 10;
int b = a / 0;
} catch (Exception e){
a = 20;
return a;
} finally {
a = 30;
System.out.println("finally a:" + a);
}
a = 40;
return a;
}
}

上面这段代码返回的是

  finally a:30
  return a:20

这说明,就算有返回语句,finally还是执行了,但是此时返回的a应该是30才对啊,观察debug发现,是执行到return把return语句的a替换为20后发现还有finally才去执行finally的,此时返回值已经确定。所以,finally中的语句执行在return中间;但是如果将代码改为下面这样,则返回的a也为30;

public class ExceptionTest {
public static void main(String[] args) { Student s = new Student(0);
System.out.println("return a:" + test(s).age);
} public static Student test(Student s) {
try {
s.age = 10;
int b = s.age / 0;
} catch (Exception e) {
s.age = 20;
return s;
} finally {
s.age = 30;
System.out.println("finally a:" + s.age);
}
s.age = 40;
return s;
}
}

如何自定义异常

  上面就提到了,异常分为两类,一类是运行期异常,一类是非运行期异常,所以自定义异常类一般是继承自这两类异常。以Exception为例进行分析。

  Exception类中出了5个构造方法,啥方法都没有。

    public Exception(){}
public Exception(String message) {
super(message);
}
public Exception(String message, Throwable cause) {
super(message, cause);
}
public Exception(Throwable cause) {
super(cause);
}
protected Exception(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

  这里就要去了解出现异常是打印的信息从何而来?前面讲到throw的时候就可以看出,创建一个Exception对象并throw。try catch中是自动创建了一个异常对象去对比,类型一致(或者可以向上转型)则执行catch。所以打印信息应该是new对象的时候传入的,也有默认的打印(空构造时)。

  所以我们自定义异常时,出了要继承两类异常外,还需要至少有两个构造的方法,一个空构造,一个非空构造,这样可以自定义异常输出。

  下面是一个自定义的异常类和测试用例:

public class ExceptionTest {
public static void main(String[] args){
int[] ageArr = new int[]{1,2,3,-1,130};
for(int a : ageArr){
try {
ageCheck(a);
} catch (AgeException e) {
e.printStackTrace();
}
} }
public static void ageCheck(int age) throws AgeException {
if(age > 120){
throw new AgeException("年龄超过120岁.");
}
if(age < 0){
throw new AgeException("年龄小于0岁.");
}
} } class AgeException extends Exception {
public AgeException() {
} public AgeException(String message) {
super(message);
}
}

异常注意事项

  1. 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
  2. 如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
  3. 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws

 

【Java基础】Java异常的一些总结的更多相关文章

  1. java基础之异常 · fossi

    在开发中,异常处理是一个不可绕开的话题,我们对于异常的处理已经非常熟练了,对于异常本身的概念.用法等不再赘述了,直接结合面试问题来加深对异常的理解吧. Throwable 可以用来表示任何可以作为异常 ...

  2. Java基础-Java中23种设计模式之常用的设计模式

    Java基础-Java中23种设计模式之常用的设计模式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.设计模式分类 设计模式是针对特定场景给出的专家级的解决方案.总的来说设 ...

  3. java基础---->java中正则表达式二

    跟正则表达式相关的类有:Pattern.Matcher和String.今天我们就开始Java中正则表达式的学习. Pattern和Matcher的理解 一.正则表达式的使用方法 一般推荐使用的方式如下 ...

  4. Java基础-Java中的堆内存和离堆内存机制

    Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

  5. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.

  6. Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock)

    Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在学习Java的之前,你可能已经听说过读 ...

  7. Java基础-Java中的并法库之线程池技术

    Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.

  8. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  9. Java基础-Java数据类型

    Java基础-Java数据类型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据类型的作用 数据类型就是一组值,以及这一组值上的操作,数据类型可以决定数据的存储方式,取值范围 ...

  10. 【java基础之异常】死了都要try,不淋漓尽致地catch我不痛快!

    目录 1.异常 1.1 异常概念 1.2 异常体系 1.3 异常分类 1.4 异常的产生过程解析 2. 异常的处理 2.1 抛出异常throw 2.2 Objects非空判断 2.3 声明异常thro ...

随机推荐

  1. Centos 6.2 32位机器安装新的JDK和Weblogic

    一.首先卸载自带的JDK 1.查看自带的java版本. root@admin]#java -version java version "1.6.0" OpenJDK Runtime ...

  2. git reflog 和git log :no branch git 提交方式

    git reflog 和git log的区别,外加git cherry-pick的一种用法 git reflog 可以查看所有分支的所有操作记录(包括(包括commit和reset的操作),包括已经被 ...

  3. 基于Python的TCP阻塞式echo服务器

    上述问题的出现是因为没有设置listen函数 from socket import * from time import ctime HOST = '' PORT = 21567 BUFSIZ = 1 ...

  4. chrome浏览器扩展--QQ群查看器(1)

    QQ群查看器--chrome浏览器扩展 源码及程序下载地址:http://pan.baidu.com/share/link?shareid=3636190804&uk=1678089569 关 ...

  5. swift版本hello

    import UIKit class ViewController: UIViewController { @IBOutlet var button : UIButton? //var alertVi ...

  6. shell 流程控制

    for循环: #!/bin/bash for file in $(ls /ect) do echo $file done

  7. System.out.println(对象)

    class Person { private String name; private int age; public String getName() { return this.name; } p ...

  8. Jmeter 日志设置---如何设置java协议中被测jar的日志?

    先转载一下Jmeter的日志设置: Jmeter运行出现问题可以通过调整jmeter的日志级别定位问题,但运行测试时建议关闭jmeter日志,jmeter打印日志耗费系统性能. Jmeter日志默认存 ...

  9. lr11 BUG?Failed to send data by channels - post message failed.

    问题描述   : http协议,场景运行一会之后,报错! erro信息: Failed to send data by channels - post message failed. 解决方法 :ht ...

  10. ANDROID_MARS学习笔记_S01原始版_023_MP3PLAYER003_播放mp3

    一.简介 1.在onListItemClick中实现点击条目时,跳转到PlayerActivity,mp3info通过Intent传给PlayerActivity 2.PlayerActivity通过 ...