一、异常基础(Exception)
1、何为异常
所谓异常,就是程序运行过程中发生了意料之外的事。所能做的就是从当前环境跳出并将问题交给上一级环境。比如妈妈说还剩了个土豆去把皮儿削了炒个土豆丝,我们定义了一个方法要削土豆皮 pealPotato(),接收一个Potato对象(即剩的那个土豆)来削皮。
public void pealPotato(Potato po)
{
//削土豆皮...
}
但是发现没有剩下的土豆了,即po==null,那还削个锤子。这时候削土豆皮 这个方法已经不能执行了(因为没有土豆怎么削),必须要报错,告诉上一级环境(妈妈),说没有剩的土豆了,是要出去买土豆还是换个菜就是上一级环境要考虑的问题了。
这时候就要抛出异常
throws new NullPointerException(); //所要操作的对象不存在
2、捕获异常
如果在方法内部抛出异常,这个方法将在抛出异常的过程中结束。要是不希望方法就此结束,就需要在方法内设置一个特殊的块来捕获异常。这个块称为try块
try{
//可能会产生异常的代码
}catch(Type1 e1){
//Type1情况下,要怎么处理
}catch(Type2 e2){
//Type2情况下要怎么处理
}
...
比如我们在削土豆时,可能会遇到无土豆可削的情况,也可能会有削皮刀损坏、不小心划伤手等情况
try{
//削土豆
}catch(没有土豆 e1){
//去买2斤
}catch(削皮刀损坏 e2){
//用菜刀凑活
}catch(划伤手 e3){
//暂停削土豆,包扎要紧
}catch(其他意外 e4){
//不干了,蘸大酱也挺好,削个屁的土豆
}
...
3、创建自定义异常
不必拘泥于Java自带的异常类型,可以创建自定义的异常类型。要自己定义异常,必须从已有的异常种类继承,最好是选择意思相近的异常类继承。最简单的方法是让编译器产生默认构造器。
class NullPotato extends Exception{}//不存在要削皮的土豆
class ParerDamage extends Exception{}//削皮刀损坏
class FingerHurt extends Exception{}//伤着手了
还可以定义一个接收字符串的构造器。对于异常来说最重要的就是异常的名字,在大多数情况下默认构造器已经够用了。
class ParerDamage extends Exception{
public ParerDamage(){}
public ParerDamage(String s) {super(s)}
}
4、异常说明
把方法可能会抛出的异常告知要使用此方法的程序员。比如妈妈要我们削土豆皮,我们把可能会出现的异常提前告诉她。这样给她一些心理准备,发生异常后能及时解决。我们称之为异常说明
void pealPotato(Potato po) throws NullPotato,ParerDamage,FingerHurt,OhterException{
//...
}
void pealPotato(Potato po) {//不能抛出异常(除了RuntimeException继承而来的异常),遇到困难自己解决喽
}
当然可以说明异常而不产生异常,而不能不说明异常而产生异常。你不说你遇到的困难,别人都以为你是躺赢的🤕。
5、捕获所有异常
try{
//
}catch(Type1 e1){
//
}catch(Type2 e2){
//
}catch(Type3 e3){
//
}
...
catch(Exception e4){
//
}
最后捕获Exception异常,把他放在处理程序列表的结尾,防止其他类型的异常被提前捕获。
二、重新抛出异常/异常链
catch(Exception e)
{
....
throw e; //抛出异常到更高级的环境中,后续的catch语句将会被忽略
}
虽然重新抛出了异常,但是其异常信息或异常抛出地点,仍然是原始异常产生的地点。
异常的栈轨迹可以通过printStackTrace()方法,可以输出到标准错误或者标准输出
e.printStackTrace();
e.printStackTrace(System.out);
e.printStackTrace(System.err);
重新抛出异常并将异常发生地点定义在重新抛出的地点
throw (Exception)e.fillInStackTrace();
三、异常的限制
当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常
1. 父类方法抛出异常,子类可抛可不抛,抛也必须抛异常说明里列出的那些异常
class BaseballException extends Exception{}
class Foul extends BaseballException{}
class PopFoul extends Foul{}
class Strike extends BaseballException{}
public abstract class Inning
{
public abstract void atBat() throws Strike,Foul;
}
public class StormInning extends Inning
{
public void atBat() throws Foul{};
//也可以抛出父类异常说明里的子异常,比如PopFoul是Foul的子类异常
public void atBat() throws popFoul{}
}
2. 父类方法不抛出异常,子类覆盖的方法不可抛出异常
3. 父类构造器抛出异常,子类构造器必须包含该异常
public abstract class Inning
{
public Inning() throws BaseballException{} //构造函数这里抛出BaseballException
}
class StormException extends Exception
class Rained extends StormException
public class StormInning extends Inning
{
public Storming() throws Rained, BaseballException{} //这里也必须抛出BaseballException
}
4、继承父类与实现接口抛出异常的冲突
若一个类继承了父类的某个方法,又要实现某个接口,接口方法与父类方法名称相同时,不能抛出不同类型的异常。
interface Storm{//要实现的接口
public void event() throws RainedOut;
...
}
abstract class Inning{//要继承的类
public void event() throws BaseballException{}
...
}
public class StormInning extends Inning implements Storm {
//该方法不可被实现,因为接口抛出的异常时RainedOut,而父类抛出的异常是BaseballException
//二者冲突,不可实现。要避免这样的情况发生
public void event() throws BaseballException{};
//但是可以不抛出异常
public void event(){}
//也可以抛出接口方法里的RainedOut异常,可能是最新的JDK改了接口的优先级
public void event() throws RainedOut{};
}
四、finally清理
对于一些代码,不管try块里面的异常是否抛出,都要执行某些命令,这时候便可以使用finally语句。
try {
//可能会抛出异常的模块
}catch(A a1){
面对情况A的处理方式
}catch(B b1){
面对情况B的处理方式
}
...
finally {
不管面对什么样的情况,都会执行的代码块
}
finally语句能用来做什么,垃圾回收机制,无论try块里发生了什么,内存总能得到释放。但是Java有垃圾回收机制,所以可以使用finally语句把内存之外的资源恢复到初始状态,如已经打开的文件或网络连链接,屏幕上画的图形,甚至是外部世界的开关(保证开关无论无何最终都是关闭状态)。
把try块放到while循环里,就建立了一个“程序执行之前必须要达到”的条件。
class NotEnoughException extends Exception
{
private static final long serialVersionUID = 1L;
}
/*
* 假设有一个人打劫我们,要舍财保命,但是我们又不想损失太多,又不知道歹徒的心理预期是多少,就一点一点的拿给他,
* 因为有finally语句,无论如何都可以执行,用来判断是否满足条件,从而跳出循环。
*/
public class FinallyTest {
static int count = 0; //我们已经交给歹徒的钱
static int expectation = 100; //歹徒心理预期的钱
public static void main(String[] args) {
while(true)
{
try {
System.out.println("我递给了歹徒一块,总共交了"+(++count)+"块");; //我们一块一块的掏给他
if(count < expectation) {//如果我们给的钱不够歹徒心理预期抛出一个异常,钱不够!
throw new NotEnoughException();
}
}catch(NotEnoughException e) {
System.out.println("”才"+count+"块钱,还不够“");
}
finally {//Finally语句总是能运行
if(count == expectation) { //每交完钱歹徒也会与自己心理预期比较一下,如果满足预期了
System.out.println("”算你小子识相“");
break; //歹徒放一句狠话就走了
}
else {
System.out.println("”麻利儿的,把钱全交出来“"); //如果没有满足预期,必然会继续要
}
}
}
}
}
不想写这一章了,脑子乱的要死。。。。。以后有机会再补充