异常:程序在运行过程中发生的由于外部问题导致的程序异常事件,发生的异常会中断程序的运行。
Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。
Throwable有两个体系:Exception(异常)和Error(错误)。
是程序中无法处理的错误,此类错误一般表示代码运行时JVM出现问题,应用不处理此类错误。
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
byte[] arr=new byte[1024*1024*600];
}
}
- byte:字节,1byte=8bits,表示的范围是0~255
- 创建一个byte类型的数组,数组的大小是:
1024byte * 1024 * 600=1KB * 1024 * 600=1MB * 600=600MB- 600MB超出虚拟机划出的空间大小
程序本身可以捕获并且可以处理的异常。
Exception又分为两类:运行时异常和编译异常。
对于不同的异常,java采用不同的异常处理方式:
try
{
需要被检测的代码/容易出现问题的代码块
}
catch(异常类 变量)
{
处理异常的代码/处理方式
}
finally
{
一定会执行的语句
}
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println("x="+x);
}
catch (Exception e)
{
System.out.println("除零啦");
}
System.out.println("over");
}
}
输出结果是:
除零啦
over
分析:
- 执行a/b时:产生了一个ArithmeticException()异常
- 因为该问题已有描述,可直接将异常封装成一个对象new ArithmeticException()
- 将对象抛给调用这个功能的调用者,即主函数中div(4/0)的地方
- try检测到问题,把该对象抛给catch,try中错误的下一行代码不执行
- catch中已定义Exception类型的参数e来接收该对象
Exception e=new ArithmeticException():多态- 执行catch中的处理代码
- 错误被解决,继续执行主函数中的代码
a) try没有捕获到异常时:
try代码块中的语句依次被执行;catch被跳过;如果存在finally则执行finally代码块;执行完finally代码块之后继续执行后续代码。
b) try捕获到异常时,并且存在与之匹配的catch:
try代码块出现异常之后的代码不会被执行;跳到该catch代码块执行处理;如果存在finally则执行finally代码块;执行完finally代码块之后继续执行后续代码。
c) try捕获到异常时,但不存在与之匹配的catch:
try代码块出现异常之后的代码不会被执行;如果存在finally,则其中的代码仍然被执行;finally之后的代码不会被执行;该异常交给JVM处理。
便于提高安全性,让调用去进行处理,不处理即编译失败。
//定义
class Demo
{
//在功能上通过throws的关键字声明了该功能有可能会出现问题
int div(int a,int b)throws Exception
{
return a/b;
}
}
//使用
class ExceptionDemo2
{
public static void main(String[] args)
{
Demo d=new Demo();
try{
int x=d.div(4,1);
System.out.println("x="+x);
}
catch (Exception e){
System.out.println(e.toString());
}
System.out.println("over");
}
}
输出结果是:
x=4
over
throw一个异常对象
例如:
throw new Exception();
必须要给出对应的处理动作:
a) 要么在内部try、catch处理;
b) 要么在函数上声明,让调用者处理。
一般情况下函数内出现异常,函数上需要声明
String getMessage()
//获取异常信息
String toString()
//获取异常类名和异常信息
void printStackTrace()
//获取异常类名和异常信息,以及异常出现在程序中的位置
//将此Thowable和其追溯打印到标准错误流
void printStackTrace(PrintString s)
//通常用该方法将异常内容保存在日志文件中,以便查阅
//将此Thowable和其追溯打印到指定的打印流
其实jvm默认的异常处理机制,就是在调用printStackTrace方法:打印异常的堆栈的跟踪信息。
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println("x="+x);
}
catch (Exception e)
{
System.out.println("除零啦");
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
System.out.println("over");
}
}
输出结果是:
除零啦
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Demo.div(Test.java:5)
at ExceptionDemo.main(Test.java:16)
over
a) 声明异常时,建议声明更为具体的异常,这样处理可以更具体。
b) 对方声明了几个异常,就对应有几个catch块
c) 不要定义多余的catch块(不要定义一个父类异常catch,试图处理可能出现的其他异常)
d) 如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。(因为catch块是按照顺序执行的,都被父类异常catch块捕获的话,就不能执行该异常对应的具体catch块了)
e) 建议在进行catch处理时,catch中一定要定义具体处理方式
a) 除以0的异常
//定义
class Demo
{
//在功能上通过throws的关键字声明了该功能有可能会出现问题
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException
{
int[] arr=new int[a];
System.out.println(arr[4]); //数组脚标越界异常
return a/b; //除以零的算术异常
}
}
//使用
class ExceptionDemo2
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(5,0);
System.out.println("x="+x);
}
catch (ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("被0除啦");
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("角标越界啦!!");
}
System.out.println("over");
}
}
输出结果是:
0
java.lang.ArithmeticException: / by zero
被0除啦
over
b) 角标越界的异常
抛出角标越界的异常(不继续执行除法运算)
//定义
class Demo
{
//在功能上通过throws的关键字声明了该功能有可能会出现问题
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException
{
int[] arr=new int[a];
System.out.println(arr[4]); //数组脚标越界异常
return a/b; //除以零的算术异常
}
}
//使用
class ExceptionDemo2
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,1);
System.out.println("x="+x);
}
catch (ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("被0除啦");
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("角标越界啦!!");
}
System.out.println("over");
}
}
输出结果是:
java.lang.ArrayIndexOutOfBoundsException: 4
角标越界啦!!
over
c) catch异常父类放在第一个,catch异常子类无法执行
因为项目中会出现特有的问题,而这些问题并未被Java所描述并封装成对象。
所以对于这些特有的问题可以按照Java对问题封装的思想,将特有问题进行自定义的异常封装。
需求:在本程序中,对于除数是负数,也视为是错误的是无法运算的
class Demo
{
int div(int a,int b)
{
//产生了Java自定义的ArrithmeticException异常,程序会自动抛给主函数
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
int x=d.div(4,0);
System.out.println("x="+x);
System.out.println("over");
}
}
输出结果为:
Exception in thread “main” java.lang.ArithmeticException: / by zero
at Demo.div(Test.java:6)
at ExceptionDemo3.main(Test.java:14)
//1.自定义的异常类名也加上Exception,可以增强阅读性
//2.自定义的异常应继承Exception类
//3. 建立该异常类,可用于生成异常对象
class FuShuException extends Exception
{
}
class Demo
{
//5.函数内部出现异常,需要在函数上声明
int div(int a,int b)throws FuShuException
{
//4. 自定义的异常,Java不认识:需要手动建立对象并通过关键字throw抛出
if(b<0){
throw new FuShuException();
}
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
//6. 调用者进行try、catch处理
try {
int x=d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e){
//7. toString()打印异常的名称和信息
System.out.println(e.toString());
System.out.println("除数出现负数了");
}
System.out.println("over");
}
}
输出结果是:
FuShuException
除数出现负数了
over
class Throwable
{
private String message;
Throwable(String message)
{
//构造一个具有指定的详细消息的新的throwable
this.message=message;
}
public String getMessage()
{
//返回此throwable的详细消息字符串
return message;
}
}
class Exception extends Throwable
{
//3.将这个message传给父类,因为父类中已经定义好了相关函数
Exception(String message)
{
//1.子类的构造函数默认第一行有一条隐式的构造函数super()
//2.如果父类中没有空参数的构造函数,可以手动定义super语句
super(message);
}
}
//创建类Exception的对象
new Exception("haha").getMessage();
class FuShuException extends Exception
{
FuShuException(String msg)
{
super(msg);
}
}
new FuShuException("错误信息").getMessage();
class FuShuException extends Exception
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0){
throw new FuShuException("出现了除数是负数的情况———— /by FuShu");
}
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
try {
int x=d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e){
//toString()打印异常的名称和信息
System.out.println(e.toString());
System.out.println("除数出现负数了");
}
System.out.println("over");
}
}
输出结果是:
FuShuException: 出现了除数是负数的情况———— /by FuShu
除数出现负数了
over
需求:异常信息里面打印每次异常时具体的负数
class FuShuException extends Exception
{
private int num;
FuShuException(String msg,int num)
{
super(msg);
this.num=num;
}
public int getNum()
{
return num;
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0){
throw new FuShuException("出现了除数是负数的情况———— /by FuShu",b);
}
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
try {
int x=d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e){
//toString()打印异常的名称和信息
System.out.println(e.toString());
System.out.println("错误的负数是:"+e.getNum());
}
System.out.println("over");
}
}
输出结果是:
FuShuException: 出现了除数是负数的情况———— /by FuShu
错误的负数是:-1
over
自定义异常:
必须是自定义类继承Exception(或Error/Throwable)
继承Exception的原因:
异常体系有一个特点:因为异常类和异常对象都需要被抛出
(函数内部抛出异常对象给函数,函数抛出异常类给调用者)
他们都具备可抛性,这个可抛性是Throwable这个体系中独有的特点
只有这个体系中的类和对象可被throws和throw操作
throws使用在函数上,throw使用在函数内
throws后面跟的是异常类,可以跟多个,用逗号隔开;
throw后面跟的是异常对象
无论是否捕获异常,finally代码总会被执行。
如果try代码块或者catch代码块中有return语句时,finally代码块将在方法返回前被执行。
class FuShuException extends Exception
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("除数为负数");
return a/b;
}
}
class ExceptionDemo5
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
return;
}
finally
{
System.out.println("finally");//finally中存放的是一定会被执行的代码
}
System.out.println("over");
}
}
输出结果是:
FuShuException: 除数为负数
finally
finally代码块:定义一定执行的代码
通常用于关闭资源
public void method()
{
连接数据库;
数据操作
(可能:要删除的数据不存在;要添加的数据不符合规则...)
throw new SQLException();
(抛出异常,程序要被停止)
关闭数据库
(无论数据操作是否成功,一定要执行关闭数据库)
}
public void method()
{
try
{
连接数据库;
数据操作 throw new SQLException();
}
catch(SQLException e)
{
会对数据库进行异常处理;
}
finally
{
关闭数据库
}
}
try
{
}
catch(异常类 异常对象)
{
}
try
{
}
catch(异常类 异常对象)
{
}
finally
{
}
try
{
}
finally
{
}
那么子类的覆盖方法只能抛出父类的异常/父类异常的子类/不抛
class AException extends Exception
{
}
class BException extends AException
{
}
class CException extends Exception
{
}
class Fu
{
void show()throws AException
{
}
}
class Zi extends Fu
{
void show()throws AException/BExcpetion/不抛
{
}
}
为什么有这个规律?
class AException extends Exception
{
}
class BException extends AException
{
}
class CException extends Exception
{
}
class Fu
{
void show()throws AException
{
}
}
class Test
{
//调用了类Fu中的show函数,选择在内部处理AException异常
void function(Fu f)
{
try
{
f.show();
}
catch(AException e)
{
}
}
}
class Zi extends Fu
{
//抛出了CException异常
void show()throws CException
{
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Test T=new Test();
T.function(new Fu());//异常的处理没问题
T.function(new Zi());//多态:Fu f=new Zi();
//异常处理有问题,function方法不能处理CException异常
}
}
那么子类在覆盖该方法时,只能抛出父类异常的子集/子集的子类
那么子类在覆盖方法时,也不可以抛出异常
如果子类方法发生了异常,就必须要进行try处理,绝对不可以抛出