什么是异常

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

异常的分类

  所有的异常类是从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. 走进WCF一 (异常如此多娇,引无数码农竞折煞)

    对于WCF一直都是只知其然,公司框架的架构者也只是对我们授之以鱼,而不授之以渔. 带着初学者的态度进入了大神Artech的博客,逐步慢慢上手. 我的解决方案(和大神的一模一样,只是过程中一波三折的) ...

  2. BZOJ 3343教主的魔法

    Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的 ...

  3. CODEVS 1069关押罪犯

    题目描述 Description S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极 不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨 ...

  4. AngularJs项目

    AngularJs项目实践总结 今年3月接触AngularJs,并且在6月的项目中开始应用,从踩坑到填坑花了不少时间,根据项目中的实际应用情况总结了一些经验,如下: 一.UI控件选择 Angularj ...

  5. CSS随手记

    html5模板 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <t ...

  6. [转载]在C#中使用官方驱动操作MongoDB

    在C#中使用官方驱动操作MongoDB 8.1)下载安装 想要在C#中使用MongoDB,首先得要有个MongoDB支持的C#版的驱动.C#版的驱动有很多种,如官方提供的,samus. 实现思路大都类 ...

  7. PHP设置COOKIE的HttpOnly属性

    httponly是微软对cookie做的扩展.这个主要是解决用户的cookie可能被盗用的问题. 大家都知道,当我们去邮箱或者论坛登陆后,服务器会写一些cookie到我们的浏览器,当下次再访问其他页面 ...

  8. Linux(CentOS)常用命令

    http://fedoranews.org/alex/tutorial/rpm/3.shtml rpm.org rpm -qa|grep mysql 查询已安装的含有mysql的包. mv 移动文件. ...

  9. POJ 2195 Going Home(最小费用最大流)

    http://poj.org/problem?id=2195 题意 :  N*M的点阵中,有N个人,N个房子.让x个人走到这x个房子中,只能上下左右走,每个人每走一步就花1美元,问当所有的人都归位了之 ...

  10. [状压dp]HDOJ4539 郑厂长系列故事——排兵布阵

    中文题,题意不再赘述 对于“?”这一格,它所能攻击到的(曼哈顿距离为2的) 前方的 即“√”的四个位置 那么与此格有关的即它前方两行(即状压这两行) 首先预处理每行能满足的: i 和(i<< ...