世界微尘里,吾宁爱与憎
异常处理&断言
异常处理&断言

异常处理&断言

异常处理

异常即指在程序的正常流程中被中断的事件,叫做异常

如果由于出现错误而使得某些操作没有完成.程序应该

  • 返回一种安全状态,并能够让程序执行其他的命令
  • 允许用户保存所有工作的结果,并以妥善的方式终止程序

而这些错误可以有下面这几种情况:

  • 用户输入错误
  • 设备错误
  • 物理限制
  • 代码错误

在发生这些错误的时候,Java就可能会抛出一个封装了错误信息的对象

异常分类

这些异常对象都是派生于Throwable类的一个类实例,换句话说,所有的异常类都是Throwable这个类的一个子类

在Throwable类下一层又立即分为两个分支,Error类和Exception

Error类描述了Java运行时系统的内部错误和资环耗尽错误,你的应用程序不应该抛出这种类型的对象,如果出现了这样的内部错误,除了通知用户,并尽力妥善地终止程序之外,你几乎无能为力

但很幸运的是,这种情况一般是很少出现的

而Exception类有分解为两个分支,一个分支派生于RuntimeException,另一个分支IOException包含其他异常,前者是有编程错误导致的异常,比如错误的强制类型转换,数组越界,访问null指针,后者是程序本身没有问题,由于用户的输入I/O这类问题导致出现的异常,比如打开一个不存在的文件,试图超越文本结尾读取数据

同时,Java语言规范把派生于Error类或RuntimeException类的所有异常统称为非检查型异常,而把其他的异常称为检查型异常,这与之后什么时候需要声明异常有关

声明及抛出异常

如果遇到了无法处理的情况,Java方法可以抛出一个异常,使用关键字throw

具体的使用流程为:

  • 找到一个合适的异常类
  • 创建这个类的一个对象
  • 讲对象抛出

比如:

var e = new EOFxception;
throw e;

也可以合并成一句

throw new EOFxception;

将这段代码放在你的方法里的任何位置并执行便可以把异常抛出

这里再补充一个概念,对于一个非检查型错误(Error+运行时错误),Java编译器会自动将其抛出,并不需要我们对其进行手动抛出,但是,对于检查性错误(IOException),编译器是不会自动将其抛出的

所以,如果你在某个方法中加入或调用了一段抛出异常的代码,而这个异常又恰好是检查型错误,编译器就不会将其自动抛出,你需要做的,就是在方法首部之后声明该方法可能会抛出这样一个检查型异常,使用throws关键

public FileInputStream(String name) throws FileNotFoundException{
  ...
       throw new FileNotFoundException;
  ...
}

如果个方法抛出多个检查异常类型,必须在方法的首部列出所有的异常类型,用逗号隔开

当然,这样做只是一个推荐,并不是必需的,如果你不声明这个异常,最多导致的结果是出现了异常的话就不会抛出异常自然也没有办法捕获到这个异常,也就不能在发生了这个异常后做出一些后续的操作

比如下面这段代码

public class A{
void test1() throws Son1Exception{
throw new Son1Exception();
}
public static void main(String[] args){
A a=new A();
try{
a.test1();
}
catch(Son1Exception()){
System.out.print("异常1");
}
}

如果把test1()后面的throws Son1Exception去掉,再运行程序,此时程序仍然可以通过编译,但是,对面之后catch(Son1Exception()这段代码及之后的代码就失去了意义

那么这里就出现了一个问题,如果你在自己编写的某个方法里,使用了另一个函方法,而这另一个方法又声明了抛出了一些检查型异常,那么你的处理方式有两种,一种是捕获这些异常并处理他们,这点在捕获异常中说,而如果你不知道怎么去处理这个异常的话,最好的方法是,你需要在你的方法首部的throws列表中添加你所使用的方法抛出的异常,这叫做传递异常

但是,有可能这个方法用十年也不会遇到抛出错误的情况,而你自己编写的这个函数由于在首部加入了这个错误,就会导致在别的地方调用你这个自己编写的函数时,也需要处理或者继续传递这个错误,否则编译器就会报错,这无疑是很繁琐的一件事情,对于那种一般并不会出现的异常来说,是很不值得为其付出这么大精力的,因此,你也可以将这个异常关闭,具体做法就是类似上面说的这样去忽略掉这个异常

然而在实际的生产中,这样做是很危险的一件事,所以,具体应该怎样,应结合实际情况去考虑,当你忽略掉了某一个异常时,你最好祈祷千万不要在此处发生异常

捕获异常

如果程序抛出了一个异常,但是没有在任何地方捕获这个异常,程序就会终止,并在控制台上打印一个消息,其中包括这个异常的类型和一个堆栈轨迹

而要想捕获一个异常,需要设置try/catch语句

public void read(String filename){
   try{
       var in =new FileInputStream(filename):
       int b;
       while((b = in.read()) != -1){
           process input
      }
  }
   catch(IOExcept exception){
       exception.printStackTace();
  }
}

如果try语句块中的任何代码抛出了catch子句中指定的一个异常类,那么

  • 程序将跳过try语句的其余代码
  • 程序将执行catch子句中的处理器代码

如果try语句中的代码抛出异常,那么程序将会跳过catch语句

如果抛出了一个catch子句没有指定的异常类,那么这个程序就会立即退出

你也可以捕获多个异常

try{
  ...
}
catch(...){
  ...
}
catch(...){
  ...
}
catch(...){
  ...
}
finally{
  ...
}

代码抛出这个异常后,无论后续做出什么处理,都会停止执行该方法后续的代码,并退出这个方法,而如果这个方法以及获取或者改变了某些本地资源,就应该将这些本地资源清理释放或者还原,这些事情由finally子句完成

不管是否有异常被捕获,finally子句都会执行

创建异常

你的代码可能会遇到任何异常类都无法描述清除的问题,在这种情况下,你可以创建一个自定义的异常类

需要做的只是定义一个派生于某个Exception类的子类,这个子类需要包含两个构造器

  • 默认构造器
  • 包含详细描述信息我的构造器
class FileFormatException extends IOException{
   public FileFormatException(){}//默认
   public FileFormatException()(String gripe){
       super(gripe);
  }
}

现在你就可以抛出这个FileFormatException类了

而这个String gripe,是在抛出这个异常时可以传入的参数,用于对这个异常进行一些描述,在具体的实现在所有异常类的超类Throwable中以及有了定义,所以调用super(gripe)即可

分析堆栈轨迹元素

堆栈轨迹是程序执行过程中某个特定点上所有挂起的方法调用的一个列表,如果想要查看该堆积轨迹元素,可以调用异常类的printStackTrace方法访问堆积轨迹的文本描述信息

注意

  • 异常处理不能代替if-else测试语句,因为异常相对于if-else语句来说,捕获异常的时间大大超过了if-else测试语句,因此,我们最好只在必须抛出异常的情况下才使用异常处理,比如,将一个空栈弹出会抛出错误,但是,我们也可以使用if-else语句实现检测一下其是否为空,而不必等到程序抛出错误,这样可以大大提高运行效率

断言

断言常用于在一个具有自我保护能力的程序中

其含义是

假设确信某个属性符合要求,并且代码的执行依赖于某个属性

例如

double y = Math.sqrt(x);

这里如果在实际的程序中,需要保证x是一个非负数,而编写者又确信在之前的程序运算中得到的x是一个非负数,

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注