Java中的异常分为两大类:
1、已检查异常(Checked Exception):凡是继承自Exception类,但是不继承自RuntimeException类的异常类都叫做已检查异常;
2、未检查异常(Unchecked Exception)或叫Runtime Exception:凡是继承自RuntimeException类的异常类都叫做未检查异常。
Throwable类底下存在两个子类分别是Error类和Exception类:
1、对于Error类,表示系统错误,我们在程序中无法对其进行操作,一旦出现错误,只能是程序终止;
2、对于Exception类,我们可以编写相应的异常处理器来对异常进行及时的处理。
Java中所有的异常都会直接或间接地继承自Exception。
RuntimeException类也是继承自Exception类,它叫做运行时异常,Java中所有的运行时异常都会直接或间接地继承自RuntimeException。
异常对象生成机制讲解: 七十三-04:00
finally详解: 七十三-10:00
异常处理的一般结构是:
try
{
// ur code here
}
catch(Exception e)
{
// Exception disposal
}
finally
{
// finally code here
}
无论程序是否出现异常,finally块中的代码均会执行。
一个try后面不管有多少个catch被定义,最多仅仅会有一个catch语句块被执行!!
代码编写抛出异常对象:
package com.edu.bupt.exception;
public class Exception2
{
public void method() throws Exception
{
System.out.println("in method");
throw new Exception();
}
public static void main(String[] args) throws Exception
{
Exception2 e2 = new Exception2();
e2.method();
}
}
异常本质也是一种类,因此在抛出的时候也是首先需要new一个异常类型的对象,然后通过throw关键字对生成的异常对象进行抛出。
当抛出一个异常对象的时候,会有两种途径对其进行处理(这里的异常,可以是cheked Exception,也可以是unchecked Exception),一种是通过try...catch处理器对其进行处理,另一种就是在方法声明部分利用throws关键字将异常对象传递至调用该方法的方法中,那个方法负责对该异常对象进行后续处理。
当在main方法头部使用throws关键字进行抛出的时候异常的时候,是由虚拟机对其进行抛出。
当打印出来异常的堆栈信息的时候,最底下的信息时调用方法的最顶层,例如main函数,如下图:
对于checked exception ,必须对其进行处理,处理方式仍然是上述的两种。
对于unchecked exception , 我们可以不对其进行处理,也可以对其进行处理,推荐不对其进行处理,因为Runtime Excetpion是在编写代码的时候完全可以避免的,与其对Runtime Exception编写处理方法,不如好好检查程序,避免这种异常情况的发生。
NullPointerExcepion,是发生频率最高的Runtime Exception,它表示某一个引用变量指向了null,而你还去按照该引用变量的方式去调用其中的方法。
自定义异常:
所谓自定义异常,通常就是定义了一个继承自Exception类的子类(可以继承自RuntimeException,也可以继承自Exception,前者自定义了一个运行时异常,后者自定义了一个非运行时异常);
一般不会继承某个运行时的异常类。
七十四-21:47
package com.edu.bupt.exception;
public class MyException extends Exception
{
public MyException()
{
super();
}
public MyException(String message)
{
super(message);
}
}
对底层异常进行二次封装(例如:数据库连接异常),即可使用自定义异常,并在构造的时候传入底层异常,使之成为本地化的异常信息:
package com.edu.bupt.exception;
public class ExceptionTest4
{
public void method(String str) throws MyException
{
if (null == str)
{
throw new MyException("null String passed into method");
}
else
{
System.out.println(str);
}
}
public static void main(String[] args) throws MyException
{
new ExceptionTest4().method(null);
}
}
运行结果:
main方法不被任何方法调用,而是由JavaVM调用main方法,因此当main方法继续将异常抛出的时候,有JavaVM对main方法抛出的异常进行处理。
package com.edu.bupt.exception;
public class ExceptionTest4
{
public void method(String str) throws MyException
{
if (null == str)
{
throw new MyException("null String passed into method");
}
else
{
System.out.println(str);
}
}
public static void main(String[] args)
{
try
{
new ExceptionTest4().method(null);
}
catch (MyException e)
{
System.out.println(e.getMessage());
}
finally
{
System.out.println("异常处理完毕");
}
System.out.println("程序执行完毕");
}
}
对于异常,如果不使用try{...}catch(){...}方式而是使用向外抛出异常方式,一旦发生异常情况,程序将会立即终止,而不会向下执行;
但是相反情况,如果使用try{...}catch(){...}方式处理异常,一旦发生异常情况,程序仍然会执行程序的后续部分,例如关闭某些资源。
根据多态,上例也可以在method方法头部抛出Exception异常对象,这个是可以的,而且也符合多态的规则,同时在main方法的catch子句中填入Exception异常类。如下:
package com.edu.bupt.exception;
public class ExceptionTest4
{
public void method(String str) throws Exception
{
if (null == str)
{
throw new MyException("null String passed into method");
}
else
{
System.out.println(str);
}
}
public static void main(String[] args)
{
try
{
new ExceptionTest4().method(null);
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally
{
System.out.println("异常处理完毕");
}
System.out.println("程序执行完毕");
}
}
程序执行相同的运行结果:
catch块:
try之后可以跟多个catch块或不跟catch块,但是最多仅仅有一个catch块被执行,不会执行完一个catch块之后又去执行其他的catch块,因为try块在遇见异常的时候仅仅会抛出一种类型的异常对象。
例如:
package com.edu.bupt.exception;
public class MyException extends Exception
{
public MyException()
{
super();
}
public MyException(String message)
{
super(message);
}
}
package com.edu.bupt.exception;
public class MyException2 extends Exception
{
public MyException2()
{
}
public MyException2(String message)
{
super(message);
}
}
package com.edu.bupt.exception;
public class ExceptionTest4
{
public void method(String str) throws MyException, MyException2
{
if (null == str)
{
throw new MyException("null String passed into method");
}
else if ("hello".equalsIgnoreCase(str)) // 这里经常讲常量放在前面是一种好的习惯,免去了判断变量是否为空指针的代码
{
throw new MyException2("hello String passed into method");
}
else
{
System.out.println(str);
}
}
public static void main(String[] args)
{
try
{
new ExceptionTest4().method(null);
}
catch (MyException e)
{
System.out.println("catch: 1");
System.out.println(e.getMessage());
}
catch (MyException2 e)
{
System.out.println("catch: 2");
System.out.println(e.getMessage());
}
finally
{
System.out.println("异常处理完毕");
}
System.out.println("程序执行完毕");
}
}
运行结果:
技巧: 对于判断变量相等之类的函数调用操作,一般来说是尽量将常量放置在前面,这样省去了对变量是否为空指针的判断代码,如上例,又如:null == var; 七十五-08:00
注意特例如下:
package com.edu.bupt.exception;
public class ExceptionTest4
{
public void method(String str) throws Exception
{
if (null == str)
{
throw new MyException("null String passed into method");
}
else if ("hello".equalsIgnoreCase(str)) // 这里经常讲常量放在前面是一种好的习惯,免去了判断变量是否为空指针的代码
{
throw new MyException2("hello String passed into method");
}
else
{
System.out.println(str);
}
}
public static void main(String[] args)
{
try
{
new ExceptionTest4().method("Hello");
}
catch (MyException e)
{
System.out.println("catch: 1");
System.out.println(e.getMessage());
}
catch (MyException2 e)
{
System.out.println("catch: 2");
System.out.println(e.getMessage());
}
catch (Exception e)
{
System.out.println("catch: 3");
System.out.println(e.getMessage());
}
finally
{
System.out.println("异常处理完毕");
}
System.out.println("程序执行完毕");
}
}
运行结果:
抛出的Exception还是与MyException2 的 catch块匹配,而没有与Exception 的 catch块匹配,记住该结论即可,自己理解,具体由JVM内部实现。加入Exception的原因是为了去掉编译时的错误,否则将不通过编译,当然也无法执行,虽然Exception 的 catch块永远不会执行到。
另外我的猜测是通过抛出的对象的引用变量获取其Class对象,然后根据Class对象是否相同对catch块中的异常类进行匹配,而不是根据指定的异常类型进行匹配,这里还是用到了多态。
变换一下:
这样会发生编译错误,因为第一个catch块Exception已经将异常进行捕获,后续的catch块不会再对其进行捕获处理了(catch按顺序进行匹配)。
所以在编写catch块的时候,尽量将最底层的子类异常catch块写在最上面,越靠近父类异常的catch块则写在最下面。 七十五-15:00
"我们可以使用多个catch块来捕获异常,这时需要将父类型的catch块放到子类型的catch块之后,这样才能保证后续的catch块可能被执行,否则子类型的catch块将永远无法到达,Java编译器会报编译错误."
如果多个catch块的异常类型是独立的(即不存在继承关系),那么先后顺序将无所谓.
关于try..catch...finally 与 return相结合的执行顺序 七十五-24:00
结论:如果try块中存在return语句,那么首先也需要将finally块中的代码执行完毕,然后方法再返回。
System.exit(0);函数终止了JVM的运行,详见 七十五-28:00
结论:如果try块中存在System.exit(0)语句,那么就不会执行finally块中的代码,因为System.exit(0)会终止当前运行的Java虚拟机,程序会在虚拟机终止前结束执行。
帮助文档:
在catch语句块中对异常进行捕获之后可以继续向外抛出异常.(在实际项目开发中非常常见) 七十五-33:00
package com.edu.bupt.exception;
public class ExceptionTest5
{
public void method() throws MyException
{
try
{
System.out.println("进入到了try块");
}
catch (Exception e)
{
System.out.println("发生异常");
throw new MyException("自定义异常");
}
finally
{
System.out.println("进入到了finally");
}
System.out.println("后续代码");
}
}
对于checked exception,JDK帮助文档中,方法声明的头部会书写throws [Exception];
但是,对于unchecked exception,JDK帮助文档中,方法声明的头部不会书写throws [Exception];