包括老师课上的补充和自己查的资料
6-1健壮性与正确性
1.什么是健壮性和正确性
保证健壮性:1.封闭实现细节,限定用户的恶意行为(不要把内部细节暴露出去)
2.考虑极端情况,没有“不可能”(错误的输入要进行处理)
正确性与健壮性:如输入了一个错误的输入,正确性可能直接报错,健壮性可能假设正确的输入是什么而得到结果或让用户重新输入。
用户更喜欢健壮性,程序员更喜欢正确性。
需要知道正确性和健壮性的对比和不同的用户两者的影响
从接口角度:接口有两种类型的接口:1)对外的接口(面向用户的,提供给客户端的)2)对外的接口。
对外的接口倾向于健壮性,对内的接口倾向于正确性。
可靠性:正确性+健壮性
如何过滤缺陷:
step0:编程时:使用断言、防御性编程、代码检查、正式验证等让程序更加安全
step1:程序编好后:通过test等检测错误并修正
step2:程序运行时:通过异常
1.如何度量健壮性和正确性
MTBF:平均失效时间
在三个阶段:coding, testing, debugging通过不同的手段保证程序的健壮性和正确性
6-2错误与异常处理
1.Error和Exception
都是从Throwable接口继承的。==1.error:==出现的错误不是由程序引起的(程序正确,由外部环境引起的);2.exception:程序引起的错误 1)程序本身的错误(如除数写成零)RuntimeException,2)调用方出错(如接口只能传整数但是传入了小数,这时程序没有错误,但是调用错误)
在大多数时候,程序员不需要实例化Error
.
3.异常Exception Handling
如图:数组越界错误,程序执行到该语句自动发现错误然后创建一个数组越界的异常对象,抛出,程序挂起。接下来查找有没有方法能够处理异常,如果处理不了就往调用方传,到控制台前没有任何程序可以处理这个异常(控制台捕获异常),就在控制台报错并停止。
正常情况下其实在程序中不应该捕获这种异常(创造一个异常来捕获自己写错的地方?)。(这里举例),应该捕获的是如client端传入的参数的异常
这个异常是runtime Exception。
3.1什么是Exception?
异常处理路径和正常执行路径放在一起,而不一定设另一个单独的catch,不用异常的方式,违背了程序的职责分配:单一职责,
如图,读的文件不存在出现一个错误
应该清晰划分一下:
用异常可以把程序区分成两部分:错误执行路径和正常执行路径,
3.2异常的分类
异常分两类:runtimeException和其他异常
runtimeException不需要捕获,其他异常必须捕获
3.3Checked and unchecked exceptions
checked Exceptions编译器帮助检查你的程序是否已抛出或处理了可能的异常(编译器告诉你必须要去捕获否则不能编译)
Unchecked Exceptions编译器不会检测(编译没有问题(但是执行的时候可能程序失败))
Unchecked异常也可以使用throws声明或try/catch进行捕获,但大多数时候是不需要的,也不应该这么做。
通过客户端会怎样补救来决定采用checked还是unchecked
如果客户端可以通过其他的方法恢复异常,那么采用checkedexception;
如果客户端对出现的这种异常无能为力,那么采用uncheckedexception;
checked异常要给客户端提供充分的信息来说明出现了什么错误。可以设置一个具体的checked异常的子类来记录具体的错误的信息。
checked 异常可预料不可预防,
Unchecked异常可预料可预防
考试的时候可能选择这些点
3.4异常的定义
throws用在方法的声明里,规约里要写。Java中任何一个 程序都不会有throws声明抛出一个Runtime异常,因为Runtime异常时程序员写程序写错了。
如果一个方法可能抛出多个已检查的异常类型,则必须在报头中列出所有异常类
抛出异常的两种情况:其他函数传来的异常,自己造成的异常。
不要抛出Unchecked异常和Error。
LSP中对异常的要求:如果父类型中某个方法抛出了异常,子类型中重写这个方法可以抛出异常也可以不抛出。但是如果抛出这个异常要和父类型抛出的异常一样或更具体。(子类型可以对父类型中不能解决的问题进行处理——不抛出异常;不变——抛出和父类型一样的异常;父类型中不能做的子类型能做一部分——可能抛出比父类型更具体的异常)但是不允许父类型没有抛出某些异常而子类型抛出了。
3.5如何抛出异常throw
3.6创建异常类
1.写一个异常类
2.在程序中throws语句抛出
3.在方法声明中加入throws语句来告诉调用方可能抛出一个异常
3.7Catching Exceptions
try{
code
more code
more code
}
catch(ExceptionType e){
handler for this type
}
通过try和catch 将程序的正常处理路径和异常处理路径区分开
异常的处理:
1.在异常内部进行处理
2.也可以不在本方法内处理,而是传递给调用方,由client处理(“推卸责任”)
如果1和2都不采用就会编译器报错
可以通过==e.getMessage()==来获得异常的具体信息,==e.getClass().getName()==来获得异常的具体类型
catch可以捕获多个异常,这时要按照优先级(由具体到宽泛)来匹配
3.8Rethrowing and Chaining Exceptions重新抛出
当抛出的异常对于用户来说并不是很确切或不合适的,可以重新抛出一个更合适的异常
但是,更好的方法是:
注:initCause这个方法是对异常进行包装的,可以记录根原因。包装后可以y用getCause()方法来获得。
如上图,如果要获得根原因:
Java中关于initCause的用法说明
3.9finally Clause
当异常抛出程序中断的时候有打开资源没有关闭,需要采取方案让资源关闭:
如果在catch语句中写关闭资源的程序:有两个问题。1.关闭的语句出现在两个地方:try(程序有可能正常执行)和catch里,所以关闭语句出现在很多地方2.catch语句需要全部的异常才能够让程序中断的时候资源一定关闭,但是catch语句是不能捕获全部的异常的
所以要用finally语句:无论发不发生异常,这条语句都会执行。
在使用finally语句是也可以没有catch
3.10The Try-with-Resources Statement
还是以3.9中资源关闭为例:
在try后面加一个声明,这是无论出不出现异常都会关闭资源。
如try (FileInputStream fis = new FileInputStream(filePath);
InputStreamReader inReader = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(inReader)😉
try后面就可以包含多个资源,对多个资源都进行关闭
java TWR是怎么优雅我们的代码的?
3.11Analyzing Stack Trace Elements
通过调用栈把调用方法的顺序描述出来(后进先出的顺序把调用的方法压到调用栈)
异常在进行传递的时候也是按调用栈的顺序
如图当方法C中除数为0出现异常,程序中断在控制台打印的顺序,先打印出了出现异常的方法C,C处理不了这个异常,就向上传递到B,发现main处理不了所以传递给控制台中断程序并打印信息。假设方法A可以处理这个异常,程序又把控制权交给程序,执行方法A中剩下的内容