一、异常的分类
二、异常的处理
针对异常,JVM的处理方式:
一旦遇到了程序出现了问题,就会把问题的类名,错误原因,错误的位置等信息打印在控制台,以便我们纠错。同时JVM会自动从出现问题处将程序终止掉,导致程序无法继续向下执行。
eg:
public class TestTryCatch {
public static void main(String[] args) {
System.out.println("程序开始了");
String str = null;
System.out.println(str.length());
System.out.println("程序结束了");
}
}
为了让其他没问题的程序继续执行可以采用以下做法:
使用try——catch语句对异常进行捕获
public class TestTryCatch {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = null;
System.out.println(str.length());
}catch(NullPointerException e){
System.out.println("出现了空指针");
}
System.out.println("程序结束了");
}
}
<2.1.1> java异常处理机制中的try——catch语法:
- try{
- 代码片段
- }catch(xxxException e){
- 捕获try代码片段中出现的xxxException
- 后的处理手段
- }
注意事项了解一下:
1.在try代码块中,发生异常语句后面的代码是不会被执行的。
2.catch可以定义多个,针对不同的异常有不同的处理手段时,可以分别捕获这些异常。
3.应当有一个好习惯,在最后一个catch中捕获Exception,尽量避免因为一个 未捕获的异常导致程序中断。捕获顺序应是子类型异常在上面先捕获,父类型异常在下后捕获。
<2.1.2> finally
在程序中,有时候我们希望有些语句无论程序是否发生异常都要执行,这时就可以在
try-catch语句后加一个finally代码块。
@Test
public void testFinallyDemo1(){
System.out.println("程序開始了");
try{
String str = null;
System.out.println(str.length());
}catch(Exception e){
System.out.println("程序出错了");
}finally{
System.out.println("finally中的代码运行了");
}
System.out.println("程序结束了");
}
finally中需要注意的:
- finally块是异常捕获机制中的最后一块。
- finally可以直接跟在try后面或者最后一个catch之后。
- finally可以保证只要程序执行到try语句块中,无论try语句块中的代码是否抛出异常,finally块中的代码都必定执行。
- 通常会将无论是否出现异常都要运行的代码放在finally中确保运行,比如IO操作中的关闭流。
- 有一点需要注意一下,finally中的代码有一种情况下是不会执行的,那就是在
try-catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。
<2.1.3> throws
如果调用自己的方法,我们很清楚该方法可能会发生的异常。试想一下,如果去调用一个别人写的方法时,是否能知道别人写的方法是否会有异常呢?这是很难做出判断的。针对这种情况,Java中允许在方法的后面使用throws关键字对外声明该方法有可能发生的异常,这样调用者在调用时,就明确的知道方法有异常,并且需要调用者对异常进行处理,否则编译无法通过。
eg:
从该例可以看出,当在方法声明抛出一个异常时,在主函数中调用时,必须对该异常进行处理,否则编译不会通过。一般在主函数调用声明异常的方法时,选择对其进行
try-catch处理。
注意事项:
派生类在继承超类时会重写其方法,那么在重写超类中含有throws声明异常抛出的方法时,对throws的重写规则:
- 可以不再抛出任何异常
- 可以仅抛出父类方法抛出的部分异常
- 可以抛出父类方法抛出异常的子类型异常
- 不允许抛出父类方法抛出异常的父类型异常
- 不允许抛出额外异常
public class ThrowsDemo {
public void dosome() throws IOException,AWTException{
}
class Son extends ThrowsDemo{
// public void dosome() throws IOException,AWTException{
//
// }
/*
* 可以不再抛出任何异常
*/
// public void dosome(){
//
// }
/*
* 可以仅抛出父类方法抛出的部分异常
*/
// public void dosome() throws IOException{
//
// }
/*
* 可以抛出父类方法抛出异常的子类型异常
*/
// public void dosome() throws FileNotFoundException{
//
// }
/*
* 不允许抛出额外异常
*/
//public void dosome() throws SQLException{} SQLException并非继承自Throwable,而是继承自 java.lang.Exception。
/*
* 不允许抛出父类方法抛出异常的父类型异常
*/
// public void dosome() throws Exception{}
}
<2.1.4> throw
当不满足业务逻辑要求时,可以主动抛异常。当一个方法中使用throw抛出一个异常时,就应该在当前方法上使用throws声明该异常的抛出,通知调用者在调用当前方法时要处理异常(RuntimeException除外)否则编译不通过。
eg:
/*
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) throws Exception{
if(age<0||age>100){
throw new Exception("年龄不合法");
}
this.age = age;
}
}
@Test
public void testThrowDemo(){
System.out.println("程序开始了");
Person p = new Person();
try {
p.setAge(100000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("此人年龄:"+p.getAge());
System.out.println("程序结束了");
}
三、自定义异常
JDK中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况。为了解决这个问题,在java中允许用户自定义异常,但自定义异常必须继承自Exception类或其他子类。
/**
* 年龄不合法异常
*
* 自定义异常通常用来说明某些业务逻辑错误
*/
public class IllegalAgeException extends Exception{
/**
*
*/
private static final long serialVersionUID = 1L;
public IllegalAgeException() {
super();
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
注意:在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类
,在构造方法中使用supper()语句调用Exception的构造方法即可。
既然自定义了异常,那么该如何使用呢?这时就需要用到throw关键字,throw关键字用于在方法中声明抛出异常的实例对象。
- 语法格式:
- throw Exception 异常对象
eg:
/*
* 使用当前类测试异常的抛出
*/
public class Person {
private int age;
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) throws IllegalAgeException{
if(age<0||age>100){
throw new IllegalAgeException("年龄不合法");
}
this.age = age;
}
}
测试异常的抛出:
因为首先我们自定义了异常类,且在Person类中使用了throw关键字在方法中向调用者抛出了IllegalAgeException异常,所以在调用该方法p.setAge()时,调用者必须处理异常,否则编译不通过。
/*
* 测试异常的抛出
*/
public class ThrowDemo {
@Test
public void testThrowDemo(){
System.out.println("程序开始了");
Person p = new Person();
/*
* 当调用一个含有throws声明异常抛出的方法时,编译器要求必须处理这个异常
* 而处理方式有两种:
* 1.使用try——catch处理该异常
* 2.在当前方法上继续声明throws将该异常抛出
* 具体如何处理,结合实际业务需求而定。
*/
try {
p.setAge(10);
} catch (IllegalAgeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("此人年龄:"+p.getAge());
System.out.println("程序结束了");
}
}
Tips
finally和final的区别:
final,最终的意思。可以修饰类,方法,变量。
修饰类,类不能被继承,但可以继承别人。
修饰方法,方法不能被重写。
修饰变量,变量是常量。
finally
异常处理的一部分。被finally控制的代码块一定会执行。
throw和throws的区别:
throws:
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常。
throw:
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常对象名
- 表示抛出异常,由方法体内的语句处理
- throw是抛出了异常,执行throw则一定抛出了某种异常
- 如果throw的是编译时期异常,在方法声明上必须用throws进行标记
- 如果throw的是运行时期异常,在方法声明上可以用throws进行标记,也可以不用。