异常
1.1 异常概念
异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:
- 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
1.2 异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable
,其下有两个子类:java.lang.Error
与java.lang.Exception
,平常所说的异常指java.lang.Exception
。
Throwable体系:
- Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
- Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
Throwable中的常用方法:
-
public void printStackTrace()
:打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
-
public String getMessage()
:获取发生异常的原因。提示给用户的时候,就提示错误原因。
-
public String toString()
:获取异常的类型和异常描述信息(不用)。
出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。
1.3 异常分类
/*
* 异常分为编译异常和运行时期异常
* 编译异常: 调用了抛出异常的方法,不处理编译失败 (try throws)
* 运行异常: 抛出的异常是RuntimeException类,或者是他的子类
*
* 运行异常的特点:
* 方法内部抛出的异常是运行异常, new XXXException
* 方法的声明上,不需要throws语句,调用者,不需要处理
* 设计原因:
* 运行异常,不能发生,但是如果发生了,程序人员停止程序修改源代码
*
* 运行异常: 一旦发生,不要处理,请你修改源代码, 运行异常一旦发生,后面的代码没有执行的意义
*/
public class RuntimeExceptionDemo {
public static void main(String[] args) {
double d = getArea(1);
System.out.println(d);
}
/*
* 定义方法,计算圆形的面积
* 传递参数0,或者负数,计算的时候没有问题
* 但是,违反了真实情况
* 参数小于=0, 停止程序,不要在计算了
*/
public static double getArea(double r){
if(r <= 0)
throw new RuntimeException("圆形不存在");
return r*r*Math.PI;
}
public static void function(){
int[] arr = {1,2,3};
//对数组的5索引进行判断,如果5索引大于100,请将5索引上的数据/2,否则除以3
//索引根本就没有
if(arr[5] > 100){
arr[5] = arr[5]/2;
}else{
arr[5] = arr[5]/3;
}
}
}
1.4 异常的产生过程解析
第二章 异常的处理
Java异常处理的五个关键字:try、catch、finally、throw、throws
/*
* 异常的关键字
* throw 在方法的内部 抛出异常的对象
* throw 后面 必须写new 对象 必须是异常的对象,必须是Exception或者子类
*
*
* 方法中声明异常关键字
* throws 用于在方法的声明上,表明方法 可能出现异常
* 请调用者处理
* throws 后面必须写异常类的类名
*
*
* 调用了一个抛出异常的方法,调用者就必须处理
* 不处理,编译失败
*
*/
public class ExceptionDemo {
public static void main(String[] args){
int[] arr = {};
try{
int i = gerArray(arr);
System.out.println(i);
}catch (Exception e) {
System.out.println(e);
}
System.out.println("Game Over");
}
//对数组的最后索引*2,返回
private static int gerArray(int[] arr) throws Exception{
//对方法参数进行合法性的判断,进行判断是不是null
if(arr == null) {
//抛出异常的形式,告诉调用者
//关键字throw
throw new Exception("传递数组不存在");
}
//对数组进行判断,判断数组中,是不是有元素
if(arr.length == 0) {
//抛出异常的形式,告诉调用者,数组没有元素
throw new Exception("数组中没有任何元素");
}
int i = arr[arr.length-1];
return i*2;
}
}
异常的处理方式:
try...catch...finally
try{
被检测的代码
可能出现异常的代码
}catch(异常类名 变量){
异常的处理方式
循环,判断,调用方法,变量
}finally{
必须执行的代码
}
try该代码块中编写可能产生异常的代码。
catch用来进行某种异常的捕获,实现对捕获到的异常进行处理。
演示如下:
/*
* 异常的处理方式:
* try...catch...finally
* try{
* 被检测的代码
* 可能出现异常的代码
* }catch(异常类名 变量){
* 异常的处理方式
* 循环,判断,调用方法,变量
* }finally{
* 必须执行的代码
* }
*/
public class ExceptionDemo3 {
public static void main(String[] args) {
try{
function(0);
}catch(Exception ex){
System.out.println(ex);
}finally{
System.out.println("代码必须执行");
}
}
public static void function(int a)throws Exception{
if( a == 0)
throw new Exception();
System.out.println(a);
}
}
/*
* 多catch写在一起
* 细节:
* catch小括号中,写的是异常类的类名
* 有没有顺序的概念,有
*
* 平级异常: 抛出的异常类之间,没有继承关系,没有顺序
* NullPointerException extends RuntimeException
* NoSuchElementException extends RuntimeException
* ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException extends RuntimeException
*
* 上下级关系的异常
* NullPointerException extends RuntimeException extends Exception
* 越高级的父类,写在下面
*/
public class ExceptionDemo1 {
public static void main(String[] args){
int[] arr = {};
try{
int i = gerArray(arr);
System.out.println(i);
}catch (NullPointerException ex) {
System.out.println("###"+ex);
}catch(ArrayIndexOutOfBoundsException ex) {
System.out.println("!!!"+ex);
}
System.out.println("Game Over");
}
/*
* 定义方法,抛出异常
* 调用者使用try catch
*/
private static int gerArray(int[] arr) throws NullPointerException,ArrayIndexOutOfBoundsException{
//对数组判空
if(arr == null) {
//手动抛出异常,抛出空指针异常
throw new NullPointerException("数组不存在");
}
//对数组索引进行判断
if(arr.length < 3) {
//手动抛出异常,抛出数组越界异常
throw new ArrayIndexOutOfBoundsException("数组没有3索引");
}
return arr[3]+1;
}
}
/*
* 继承后,在子类重写方法的时候,异常处理
* 结论:
* 父类的方法,如果抛出异常,子类重写后
* 可以不抛出异常
* 也可以抛出异常,但是如果子类要抛,抛出的异常不能大于父类的异常
* 大于,都是指的时继承关系
*
*/
public class ExceptionDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.function();
}
}
class Fu{
public void function(){
}
}
class Zi extends Fu{
public void function(){
try {
method();
} catch (Exception e) {
e.printStackTrace();
}
}
public void method()throws Exception{
}
}
如何获取异常信息:
Throwable类中定义了一些查看方法:
/*
* Throwable类中的方法
* 三个方法,都和异常的信息有关系
* String getMessage() 对异常信息的详细描述 异常了!
* String toString() 对异常信息的简短描述 java.lang.Exception: 异常了!
* void printStackTrace() 将异常信息追踪到标准的错误流 异常信息最全,JVM默认调用方法也是这个方法
*/
public class ExceptionDemo1 {
public static void main(String[] args) {
try{
function();
}catch(Exception ex){
//System.out.println(ex.toString());
ex.printStackTrace();
}
}
public static void function() throws Exception{
throw new Exception("异常了!");
}
}
finally 代码块
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
什么时候的代码必须最终执行?
当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。
finally的语法:
try…catch…finally:自身需要处理异常,最终还得关闭资源。
注意:finally不能单独使用。
比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。
finally代码参考如下:
public class TryCatchDemo4 {
public static void main(String[] args) {
try {
read("a.txt");
} catch (FileNotFoundException e) {
//抓取到的是编译期异常 抛出去的是运行期
throw new RuntimeException(e);
} finally {
System.out.println("不管程序怎样,这里都将会被执行。");
}
System.out.println("over");
}
/*
*
* 我们 当前的这个方法中 有异常 有编译期异常
*/
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {//如果不是 a.txt这个文件
// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw
throw new FileNotFoundException("文件不存在");
}
}
}
当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。
自定义异常
概述
为什么需要自定义异常类:
我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。
在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢?
什么是自定义异常类:
在开发中根据自己业务的异常情况来定义异常类.
自定义一个业务逻辑异常: RegisterException。一个注册异常类。
异常类如何定义:
- 自定义一个编译期异常: 自定义类 并继承于
java.lang.Exception
。 - 自定义一个运行时期的异常类:自定义类 并继承于
java.lang.RuntimeException
。
自定义异常的练习
/*
* A: 自定义异常的定义
* a:通过阅读源码,发现规律:
* 每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的 封装。
* b:格式:
* Class 异常名 extends Exception{ //或继承RuntimeException
* public 异常名(){
* }
* public 异常名(String s){
* super(s);
* }
* }
*/
public class ExceptionDemo {
public static void main(String[] args) {
int avg = getAvg(50,60,-70,80);
System.out.println(avg);
}
/*
* 传递成绩,计算成绩的平均数
* 成绩没有负数,需要抛出异常,停止运算
*/
public static int getAvg(int...source){
int sum = 0 ;
for(int s : source){
if( s < 0){
throw new FuShuException("成绩错误 "+s);
}
sum = sum + s;
}
return sum/source.length;
}
}
/*
* 自定义异常
* 继承Exception,或者继承RuntimeException
* 构造方法中,super将异常信息,传递给父类
*/
public class FuShuException extends RuntimeException{
public FuShuException(String s){
super(s);
}
public FuShuException(){}
}