Java 常用类解析:java异常机制,异常栈,异常处理方式,异常链,异常丢失详解

1、java标准异常概述

Throwable表示任何可以作为异常被抛出的类,有两个子类Error和Exception。从这两个类的源代码中可以看出,这两个类并没有添加新的方法,Throwable提供了所以方法的实现。Error表示编译时和系统错误。Exception是可以被抛出的异常类。RuntimeException继承自Exception(如NullPointerException),表示运行时异常,JVM会自动抛出.

2、自定义异常类

自定义异常类方法: 通过继承Throwable或Exception。异常类的所有实现都是基类Throwable实现的,所以构造自定义异常类完全可以参考Exception和Error类。我们只要添加上自定义异常类的构造方法就可以了

<span style="font-size:16px;">package demo.others; 

/**
 * 自定义异常类方法
 * 1、通过继承Throwable
 * 2、通过继承Exception
 *
 * @author Touch
 */
public class MyExceptionDemo extends Exception { 

 private static final long serialVersionUID = 1L; 

 public MyExceptionDemo() {
  super();
 } 

 public MyExceptionDemo(String message) {
  super(message);
 } 

 public MyExceptionDemo(String message, Throwable cause) {
  super(message, cause);
 } 

 public MyExceptionDemo(Throwable cause) {
  super(cause);
 }
}
</span>

 3、异常栈及异常处理方式

可以通过try、catch来捕获异常。捕获到的异常。下面的示例演示了几种常用异常处理方式

<span style="font-size:16px;">package demo.others; 

import mine.util.exception.MyException; 

public class ExceptionDemo1 {
 public void f() throws MyException {
  throw new MyException("自定义异常");
 } 

 public void g() throws MyException {
  f();
 } 

 public void h() throws MyException {
  try {
   g();
  } catch (MyException e) {
   //1、通过获取栈轨迹中的元素数组来显示异常抛出的轨迹
   for (StackTraceElement ste : e.getStackTrace())
    System.out.println(ste.getMethodName());
   //2、直接将异常栈信息输出至标准错误流或标准输出流
   e.printStackTrace();//输出到标准错误流
   e.printStackTrace(System.err);
   e.printStackTrace(System.out);
   //3、将异常信息输出到文件中
   //e.printStackTrace(new PrintStream("file/exception.txt"));
   //4、重新抛出异常,如果直接抛出那么栈路径是完整的,如果用fillInStackTrace()
   //那么将会从这个方法(当前是h()方法)作为异常发生的原点。
   //throw e;
   throw (MyException)e.fillInStackTrace();
  }
 }
 public static void main(String[] args) {
   try {
    new ExceptionDemo1().h();
   } catch (MyException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
 }
}
</span>

运行结果:

f
g
h
main
mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo1.f(ExceptionDemo1.Java:7)
 at demo.others.ExceptionDemo1.g(ExceptionDemo1.Java:11)
 at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
 at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo1.f(ExceptionDemo1.java:7)
 at demo.others.ExceptionDemo1.g(ExceptionDemo1.java:11)
 at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
 at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo1.f(ExceptionDemo1.java:7)
 at demo.others.ExceptionDemo1.g(ExceptionDemo1.java:11)
 at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
 at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:30)
 at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)

分析上面的程序,首先main函数被调用,然后是调用h函数,再g函数、f函数,f函数抛出异常,并在h函数捕获,这时将依次从栈顶到栈底输出异常栈路径。

4、异常链

有时候我们会捕获一个异常后在抛出另一个异常,如下代码所示:

<span style="font-size:16px;">package demo.others; 

import java.io.IOException; 

import mine.util.exception.MyException; 

public class ExceptionDemo2 {
 public void f() throws MyException {
  throw new MyException("自定义异常");
 } 

 public void g() throws Exception {
  try {
   f();
  } catch (MyException e) {
   e.printStackTrace();
   throw new Exception("重新抛出的异常1");
  }
 } 

 public void h() throws IOException {
  try {
   g();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   throw new IOException("重新抛出异常2");
  }
 }
 public static void main(String[] args) {
   try {
    new ExceptionDemo2().h();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
 }
}
</span>

运行结果:

mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
java.lang.Exception: 重新抛出的异常1
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
java.io.IOException: 重新抛出异常2
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:27)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)

从结果中我们可以看出,异常栈变小了。也就是说丢失了最原始的异常信息。怎样保存最原始的异常信息呢?Throwable类中有个Throwable  cause属性,表示原始异常。通过接收cause参数的构造器可以把原始异常传递给新异常,或者通过initCause()方法。如下示例:

<span style="font-size:16px;">package demo.others; 

import java.io.IOException; 

import mine.util.exception.MyException; 

public class ExceptionDemo2 {
 public void f() throws MyException {
  throw new MyException("自定义异常");
 } 

 public void g() throws Exception {
  try {
   f();
  } catch (MyException e) {
   e.printStackTrace();
   throw new Exception("重新抛出的异常1",e);
  }
 } 

 public void h() throws IOException {
  try {
   g();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   IOException io=new IOException("重新抛出异常2");
   io.initCause(e);
   throw io;
  }
 }
 public static void main(String[] args) {
   try {
    new ExceptionDemo2().h();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
 }
}
</span>

结果:

mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
java.lang.Exception: 重新抛出的异常1
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
Caused by: mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
 ... 2 more
java.io.IOException: 重新抛出异常2
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:27)
 at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
Caused by: java.lang.Exception: 重新抛出的异常1
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
 at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
 ... 1 more
Caused by: mine.util.exception.MyException: 自定义异常
 at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
 at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
 ... 2 more

从结果中看出当获取到“重新抛出异常2的时候,同时可以输出原始异常“重新抛出的异常1“和原始异常”自定义异常,这就是异常链。

5、finally的使用

finally子句总是执行的,通常用来做一些清理工作,如关闭文件,关闭连接等

下面举几个finally的例子:

<span style="font-size:16px;">// 读取指定路径文本文件
 public static String read(String filePath) {
  StringBuilder str = new StringBuilder();
  BufferedReader in = null;
  try {
   in = new BufferedReader(new FileReader(filePath));
   String s;
   try {
    while ((s = in.readLine()) != null)
     str.append(s + '\n');
   } finally {
    in.close();
   }
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return str.toString();
 }</span>

分析:如果调用in = new BufferedReader(new FileReader(filePath));时发生异常,这时是一个文件路径不存在的异常,也就是说并没有打开文件,这时将会直接跳到catch块,而不会执行try...finally块(并不是finally子句)里面的语句in.close();此时不需要关闭文件。

再看一个例子,会导致异常的丢失

<span style="font-size:16px;">package demo.others; 

import mine.util.exception.MyException; 

public class ExceptionDemo3 {
 public void f() throws MyException {
  throw new MyException("异常1");
 } 

 public void g() throws MyException {
  throw new MyException("异常2");
 } 

 public static void main(String[] args) { 

  try {
   ExceptionDemo3 ex = new ExceptionDemo3();
   try {
    ex.f();
   } finally {
    ex.g();//此时捕获g方法抛出的异常,f方法抛出的异常丢失了
   }
  } catch (MyException e) {
   System.out.println(e);
  } 

 }
}
</span>

结果:mine.util.exception.MyException: 异常2

此时异常1就丢失了

或者这样写:

<span style="font-size:16px;">package demo.others; 

import mine.util.exception.MyException; 

public class ExceptionDemo3 { 

 public void g() throws MyException {
  throw new MyException("异常2");
 } 

 public static void main(String[] args) {
  ExceptionDemo3 ex = new ExceptionDemo3();
  try {
   ex.g();
  } finally {
   //直接return会丢失所以抛出的异常
   return;
  } 

 }
}
</span>

6、异常的限制

(1)当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常,有些基类的方法声明抛出异常其实并没有抛出异常,这是因为可能在其子类的覆盖方法中会抛出异常

(2)构造器可以抛出任何异常而不必理会基类构造器所抛出的异常,派生类构造器异常说明必须包含基类构造器异常说明,因为构造派生类对象时会调用基类构造器。此外,派生类构造器不能捕获基类构造器抛出的异常。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Java 常用类解析:java异常机制,异常栈,异常处理方式,异常链,异常丢失详解

    1.java标准异常概述 Throwable表示任何可以作为异常被抛出的类,有两个子类Error和Exception.从这两个类的源代码中可以看出,这两个类并没有添加新的方法,Throwable提供了所以方法的实现.Error表示编译时和系统错误.Exception是可以被抛出的异常类.RuntimeException继承自Exception(如NullPointerException),表示运行时异常,JVM会自动抛出. 2.自定义异常类 自定义异常类方法: 通过继承Throwable或Exc

  • Java常用类之System类的使用指南

    目录 1.System类 1. arraycopy(…)方法 2. currentTimeMillis()方法 3. gc()方法 4. exit(int status)方法 2.BigInteger 3.BigDecimal 1.System类 System系统类,主要用于获取系统的属性数据和其他操作,因其构造方法是私有的并且类中的成员方法都是静态的,所以在使用的时候不需要创建对象,可直接调用. 该类实现了一些关于系统功能的方法如下: import java.util.Arrays; /**

  • Java常用类String的面试题汇总(java面试题)

    1.比较两个字符串时使用"=="还是equals()方法? 当然是equals方法."=="测试的是两个对象的引用是否相同,而equals()比较的是两个字符串的值是否相等.简单来说,基本数据类型都可以使用==.而引用类型使用==比较不了. 2.如何将字符串转化成int? 使用包装类Integer.Integer.valueOf("2");其他基本数据类型都是类似 3.为什么在Java中存储密码要使用char[],而不使用String. 因为St

  • 带你了解Java常用类小结

    目录 Java常用类 包装类 包装类中的常量 包装类的构造方法 包装类中的常用方法 Math类 枚举 枚举定义 枚举的常见方法 总结 Java常用类 包装类 由于Java语言中的基本类型不是面向对象,并不具备对象的性质,实际使用存在很多不便.Java在java.lang包中提供了八种基本类型对应的包装类,可以方便地将它们转化为对象进行处理,并且可以调用一些方法.Java中基本类型和包装类的对应关系如下表所示: 基本数据类型名称 包装类名称 byte Byte short Short int In

  • Java常用类之字符串相关类使用详解

    目录 字符串相关类 1.String类的使用 2.理解String类源码 3.使用StringBuilder类 4.StringBuilder类源码 字符串相关类 String.StringBuilder.StringBuffer类是三个字符串相关类. String类代表不可变字符序列,StringBuilder类和StringBuffer类代表可变字符序列. 关于这三个类的详细的用法,在笔试和面试以及实际开发中经常能用到,我们必须掌握好它. 1.String类的使用 String的常用方法:

  • Java常用类之日期相关类使用详解

    目录 日期相关类 1.Date类 2.DateFormat类 3.Calendar类 日期相关类 1.Date类 在标准Java类库中包含一个Date类,它的对象表示一个特定的瞬间,精确到毫秒.在网上商城下单时,在对报销单进行审核时,都需要获取当前的时间,通过Date类就可以完成. 例子:Date类的使用 package li.normalclass.date; import java.util.Date; public class TestDate { public static void m

  • Java 利用dom方式读取、创建xml详解及实例代码

    Java 利用dom方式读取.创建xml详解 1.创建一个接口 XmlInterface.Java public interface XmlInterface { /** * 建立XML文档 * @param fileName 文件全路径名称 */ public void createXml(String fileName); /** * 解析XML文档 * @param fileName 文件全路径名称 */ public void parserXml(String fileName); }

  • Java零基础也看得懂的单例模式与final及抽象类和接口详解

    目录 1.单例模式 1.饿汉式和懒汉式的实现 2.饿汉式 3.懒汉式 4.懒汉式和饿汉式的区别 2.final的使用 1.基本介绍 2.使用场景 3.使用注意事项和细节讨论 3.抽象类 1.基本介绍 2.使用注意事项和细节讨论 4.接口 1.基本介绍 2.注意事项和细节讨论 3.实现接口与继承的区别 4.接口与继承同时出现如何访问属性 5.接口的多态特性 1.单例模式 1.饿汉式和懒汉式的实现 步骤: 将构造器私有化 类的内部创建对象 向外暴露一个静态的公共方法 2.饿汉式 class Scho

  • c++11 类中关于default、explict、implicit、noexcept、final的详解

    default default是c++11的标准,它的作用是告诉编译器声明一个无参的默认构造函数. 最初的时候我们声明类是这样的: class test{ public: int add(){} }; 由于我们没有给默认构造函数,c++编译器隐式的帮我们增加了一个默认的无参构造函数,注意这一点取决于编译,有的编译器不会增加,但大多数都会,如GCC.MSVC. 但是一旦我们声明了一个有参的构造函数: class test{ public: test(int a){} int add(){} };

  • Java BigDecimal类的一般使用、BigDecimal转double方式

    目录 BigDecimal类的一般使用.BigDecimal转double BigDecimal大据类 BigDecimal类 创建一个BigDecimal对象 方法声明 BigDecimal转double BigDecimal , double 转换方式 BigDecimal类的一般使用.BigDecimal转double BigDecimal大据类 浮点型运算的时候直接 加减乘除时可能会出现数据失真(精度问题). BigDecimal可以解决浮点型运算数据失真的问题.         dou

随机推荐