首先来说说什么是异常。
在《java编程思想》中这样定义 异常:阻止当前方法或作用域继续执行的问题。虽然java中有异常处理机制,但是要明确一点,决不应该用”正常”的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意。
比如大家经常写java代码,会碰到到IndexOutOfBoundsException(数组越界异常),会碰到的NullPointerException(空指针异常)等。这种必须编译之后才能发现的错误称为异常。java把异常都看为类,每当发现一个异常就会创建这个异常的实例。
java异常类结构如下
这里需要说明是三个类
Error:出现这种异常基本都是运行java程序的环境出现的问题。比如jvm崩溃,断网等。像这类异常通常指示合理的应用程序不应该试图捕获的严重问题。当出现这种异常时,通常不要试图去捕获,因为基本是程序处理不了的问题。
Exception:程序可以处理的异常
RuntimeException:这个Exception的子类必须单独提出来说一说。这类异常被称为运行时异常。像这类异常是编译器的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。比如IndexOutOfBoundsException(数组越界异常),会碰到的NullPointerException(空指针异常),是RuntimeException的子类,当出现这种异常时,编译器既不会提醒你抛出异常,也不会提醒你需要捕获异常。
除了这些java自己设置的异常。我们有时候还需要自己定义异常,这种异常通常是符合java语法,但是不符合我们程序的业务逻辑,这种时候就需要自己创建一个异常了。举个简单的例子:要求用户输入自己的昵称,如果用户输入的是一些非法字符(比如骂人的话),这个时候就需要自定义一个异常,当程序一发现这种异常,就将它抛出。
Java规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error
代码示例:
自己主动创建异常:
/**
* 使用该类测试抛出异常的处理方式
* 通常遇到两种情况需要抛出异常给调用者:
* 1.程序执行遇到错误,但是该异常不应当由当前代码来处理时,
* 应当抛给调用者(流程决策者)来处理
* 2.当遇到一个满足语法要求(程序本身没有异常)但是该情况不符合业余逻辑要求时,
* 可以主动创建一个异常来通知调用者不应该这样做。
* @author Analyze
*
*/
public class Person {
private int age;
public int getAge() {
return age;
}
/**
* 通常,一个方法中使用throw抛出什么异常,方法上就应当使用throws定义该异常的抛出以告知调用者需要处理该异常。
* 但是只有一类异常例外,即RuntimeException及其子类异常
* @param age
* @throws Exception
*/
public void setAge(int age) throws IllegalAgeException {
if(age<0||age>200){
throw new IllegalAgeException("年龄不在合理范围内"+age);
}
this.age = age;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
/**
* 自定义异常,通常用来定义业务逻辑层级的异常
*
* 年龄不合法异常
* @author Analyze
*
*/
public class IllegalAgeException extends Exception {
/**
* 因为父类序列化了,所以子类最好也能序列化
* 设置版本号
*/
private static final long serialVersionUID = -6878617771685349880L;
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
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
java的异常处理机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当发现异常时,在当前方法内不去解决,而是继续丢出去,交给调用这个方法的人去“擦屁股”。关键字:throws
捕捉异常:通过try-catch语句或者try-catch-finally语句实现。
在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
简单点来说,需要将有可能发生异常的语句用try-catch(异常处理器)包住。在 try中尝试执行有可能发生错误的语句,当发生异常时,查看当前异常与catch中的异常有没有一样的异常或者是catch中异常的子类。如果有,那么JVM认为开发人员意识到这个异常,就会交给开发人员去解决。如果没有,那么JVM就认为开发人员没有意识到这个异常,继续将该异常抛出。如果在程序中一直没有处理这个异常(意味着将异常抛给主函数),那么在程序运行时JVM就将该程序杀死(程序终止)。
代码实例:
java异常捕获机制中的try-catch
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str="";
/*
* 当虚拟机指定到一处错误时,会创建一个该类型异常的实例,
* 并将完成的错误报告设置好,然后将该异常抛出
*/
System.out.println("len:"+str.length());
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
/*
* 在try语句块中出错代码以下的代码都不再被执行
*/
System.out.println("!!!!!");
}catch(NullPointerException e){
/*
* catch块内容执行完毕就退出异常捕获机制
* 继续向下执行代码
*/
System.out.println("出现了空指针异常!!");
/*
* catch块可以定义多个,针对不同异常若有不同的处理手段,
* 可以定义多个catch分别捕获这些异常
*/
}catch(StringIndexOutOfBoundsException e){
System.out.println("字符串下标越界了!");
/*
* 应当有一个好习惯,在最后一个catch中捕获Exception
*/
}catch(Exception e){
System.out.println("反正就是出错了");
}
System.out.println("程序结束了");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
finally块
finally块被定义咋异常捕获机制的最后一块代码块上。
finally可以保证内部代码一定被执行。无论try块中的代码是否抛出异常。
所以通常会把诸如释放内存等操作放在finally块中。
finally可以直接跟在try块后,或者会有一个catch块之后
public static void main(String[] args) {
try{
String str=null;
System.out.println(str.length());
}catch(Exception e){
System.out.println("程序出错了");
}finally{
System.out.println("finally块代码被执行了");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
有一道关于finally块的经典面试题,通过这个题跟能说明finally块一定会被执行的问题
public static void main(String[] args) {
System.out.println(
test("0")+","+
test(null)+","+
test("")
);
/**
* !!注意:输出为3,3,3
* 代码执行过程为:
* 当执行test("0")时,调test方法
* 看见返回值为int型变量,会自己先定义一个int型的变量(例如变量名为r)
* 当执行到return str.charAt(0)-'0';这句话时,把return后计算的值赋值给r
* 然后一层一层退出
* 当跳出try看见有必须执行的finally块,就会跳进去执行块里的东西
* 然后将finally块中的return后面计算的结果重新赋值给r
* 结束程序,这样返回到的值就是为3
*
*
* 其他的输出结果原因同上
*/
}
public static int test(String str){
try{
return str.charAt(0)-'0';
}catch(NullPointerException e){
return 1;
}catch(Exception e){
return 2;
}finally{
return 3;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
Exception定义的方法
public static void main(String[] args) {
try{
Person p=new Person();
p.setAge(250);
System.out.println("21");
}catch(IllegalAgeException e){
/*
* Exception定义了方法:
* void printStackTrace()
* 该方法时用来将异常出现的代码执行过程完成输出,
* 以便于程序员追踪并修改错误
*/
// System.out.println("31");
e.printStackTrace();
// System.out.println("41");
/*
* String getMessage()
* 得到出错信息
*/
String message=e.getMessage();
System.out.println("出错了!"+message);
}
System.out.println("11");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
测试子类重写父类含有throws异常抛出的方法时多throws重写的规则
import java.awt.AWTException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
/**
* 测试子类重写父类含有throws异常抛出的方法时多throws重写的规则
* @author Analyze
*
*/
public class Father {
public void dosome()throws IOException,AWTException{
}
}
class Son extends Father{
//可以不在抛出任何异常
// public void dosome(){}
//仅抛出父类方法中抛出的部分异常
// public void dosome()throws IOException{}
//抛出父类方法抛出异常的子类异常
// public void dosome()throws FileNotFoundException{}
//不可以抛出额外异常(与父类方法抛出异常没有继承关系的其他异常)
// public void dosome()throws SQLException{}
//不可以抛出父类方法抛出的异常的父类异常
// public void dosome()throws Exception{}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28