异常
:Java 运行时期发生的问题就是异常。
Java 中运行时发生的除了异常(Exception)还有错误(Error)
异常
:通常发生可以有针对性的处理方式的
错误
:通常发生后不会有针对性的处理方式。
Error的发生往往都是系统级别的问题,都是jvm所在系统发生的并反馈给jvm的。无法针对处理,只能修正代码。
class ExceptionDemo
{
public static void main(String[] args)
{
int [] arr =new int[4];
int [] arr1 =new int[100000000000000];//ExceptionDemo.java:7: 错误: 过大的整数: 100000000000000
System.out.println(arr[0]);
System.out.println(arr[3]);//发生了ArrayIndexOutOfBoundsException,数组角标越界,导致程序无法执行,程序结束。
}
}
ArrayIndexOutOfBoundsException浅析
:
没有找到4这个角标,运行时发生了问题,这个问题jvm认识,这个问题jvm本身有描述:
描述内容:问题的名称,问题的内容,问题的发生位置。
既然有这么多的信息,java就将这些信息直接封装到对象中->ArrayIndexOutOfBoundsException
jvm需要将问题抛给调用者main,main函数就接收到越界异常对象,没有处理main函数就会继续抛出调用者jvm。
jvm收到问题后,就会做出最终的处理凡是,将问题对象中的名称,信息,问题的位置,都会显示在屏幕上,让软件使用者知道,同时让程序提前终结。
异常的应用
:
在编写程序时,必须要考虑程序的问题情况。
举例,卖水果功能需要传递钱参数,有可能有假币。
所以定义程序需要考虑程序的健壮性。
加入一些逻辑性的判断。
class Demo
{
/*
对给定的数组通过给定的角标获取元素。
*/
int getElement(int[] arr,int index)
{
/*
jvm除了问题,自己打包对象并抛出。
但是它提供得信息不够给力,想要更清晰,想要自己写。
它的抛出不满足我们的要求,准备自己抛。
*/
if(arr==null)
{
throw new NullPointerException("arr指向的数组不存在");
}
if(index<0||index>=arr.length)
{
//该条件如果满足,功能已经无法继续运算。
//这时就必须结束功能,并将问题告知调用者,这时就需要通过异常来解决。
//1,创建一个异常对象,封装一些提供信息(自定义)。
//2,需要将这个对象告知给调用者,怎么告知呢,怎么将这个镀锡传递个给调用者处呢?
//3,throw用在函数中,抛出异常对象,并可以结束函数。
throw new ArrayIndexOutOfBoundsException("帅气的角标,"+index+"在数组中不存在");
}
int element =arr[index];
return element;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
int[] arr={12,34,45};
int num=d.getElement(arr,4);
System.out.println("num ="+num);
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
if(age<0||age>200)
{
throw new IllegalArgumentException(age+",年龄数值非法");
}
this.name=name;
this.age=age;
}
//定义Person对象对应的字符串表现形式,覆盖Object中的toString方法
public String toString()
{
return "Person[name="+name+",age="+age+"]";
}
}
class ExceptionDemo4
{
public static void main(String[] args)
{
Person p=new Person("xiaoming",-12);
System.out.println(p);
}
}
自定义异常的信息
,那么异常的名字是否可以定义呢?是否可以定义更符合自己程序的阅读呢?
之前的几个异常都是java通过类进行类进行的描述,并将问题封装成对象,这是符合面向对象的思想。
其实,异常就是将问题封装成对象。
所以我也准备将自己所需要的问题进行类的描述。
发生编译失败:
ExceptionDemo5.java:28: 错误: 不兼容的类型: NoAgeException无法转换为Throwable
throw new NoAgeException(age+”,年龄数值非法”);
^
1 个错误
需要Throwable,查阅API,知道Throwable是异常和错误的超类。
原来它是异常体系的顶层类。
Throwable
|–Error
|–Exception
了解到,自定义异常被抛出,必须要继承Throwable,或者继承Throwable的子类。
该对象才可以被throw抛出。
原来这个异常体系具有一个特有的特性,可抛性,可以被throw关键字操作。
继承选择父类时,更为确切得是继承Exception。
但是还是编译失败。错误如下:
ExceptionDemo5.java:43: 错误: 未报告的异常错误NoAgeException; 必须对其进行捕获或声明以便抛出
throw new NoAgeException(age+”,年龄数值非法”);
^
1 个错误
通过这个编译失败提示,发现自定义的异常和之前使用的异常(空指针异常,角标越界异常,无效参数异常)不同。
抛出哪些异常没有这个失败提示呢?那么之前的异常和自定义的异常有什么区别?
通过查阅API的继承体系发现,之前的异常都是Exception下面的RunTimeException子类的子类。
阅读 RunTimeException 描述中明确说明,这个运行时异常以及其子类都无需进行声明。
//自定义异常,描述Person的年龄数值非法。
/*
只要时本项目的Person的年龄出现非法值就会发生该异常
*/
class NoAgeException extends RuntimeException
{
NoAgeException()
{
super();
}
NoAgeException(String message)
{
super(message);//如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
if(age<0||age>200)
{
throw new NoAgeException(age+",年龄数值非法");
}
this.name=name;
this.age=age;
}
//定义Person对象对应的字符串表现形式,覆盖Object中的toString方法
public String toString()
{
return "Person[name="+name+",age="+age+"]";
}
}
class ExceptionDemo5
{
public static void main(String[] args)
{
Person p=new Person("xiaoming",-12);
System.out.println(p);
}
}
ExceptionDemo5.java中涉及的问题:
1,继承Exception和继承RuntimeException的区别?
2,什么是捕获,什么是声明?
class Demo
{
void show()
{
//如果在函数内抛出Exception,编译失败,因为编译器在检查语法时发生了错误。
/*
该程序中已经出现问题,Java认为这个程序本身存在隐患。
需要捕获或者声明出来(要么把问题处理,要把问题标识出来让调用者知道)
*/
// throw new Exception();
/*
为什么抛出RuntimeException,不需要捕获,不要声明呢?
不是功能本身发生的异常,而是因为比如调用者传递参数错误而导致功能运行失败。
这时也是问题,需要通过异常来体现,但是这个异常不要声明出来的。
声明的目的是为了让调用者进行处理。
不声明的目的是不让调用者进行处理,就是为了让程序停止,让调用者看到现象,并进行代码的修正。
原理异常两种:
1,编译时异常,编译器会检测的异常。
2,运行时异常,编译器不会检测的异常,不需要声明,声明也可以,就是让调用者给出处理方式
ArrayIndexOutOfBoundsException
IllegalArgumentException
ClassCastException
NullPointerException
*/
throw new RuntimeException();
}
}
声明和捕获
。
声明:将问题标识出来,报告给调用者。
如果函数内通过throw抛给了编译时异常,而捕获,那么必须通过throws进行声明,让调用者去处理。
捕获:Java中对异常有针对性的语句进行捕获。
语句:
try
{
//需要被检测的语句
}
catch(异常类 变量)
{
//异常的处理语句。
}
finally
{
//一定会被执行的语句。
}
class Demo
{
/*
如果定义功能时有问题发生需要报告给调用者,可以通过在函数上使用throws关键字
*/
void show()throws Exception
{
throw new Exception();
}
}
class ExceptionDemo7
{
public static void main(String[] args)//throws Exception//在调用者上继续声明。
{
Demo d=new Demo();
try{
d.show();//当调用了声明异常方法时,必须有处理方式,要么捕获,要么声明。
}
catch(Exception e)//括号中需要定义什么呢?对方抛出的是什么问题,在括号中就定义了什么问题的引用
{
System.out.println("异常发生了");
}
System.out.println("over");
}
}
class NoAgeException extends RuntimeException
{
NoAgeException()
{
super();
}
NoAgeException(String message)
{
super(message);//如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
}
}
class Person
{
private String name;
private int age;
/*
构造函数到底是抛出这个NoAgeException是继承Exception呢?还是继承RuntimeException呢?
继承Exception,必须要throws声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行。
但是如果使用到了Person对象的数据,导致都是失败的。
继承RuntimeException,不虚要throws声明的,这时调用是不可能编写捕获代码的,因为调用根本就不知道有问题
一旦发生NoAgeException,调用者程序会停掉,并将jvm将信息显示到屏幕,让调用者看到问题,修正代码。
*/
Person(String name,int age)
{
if(age<0||age>200)
{
throw new NoAgeException(age+",年龄数值非法");
}
this.name=name;
this.age=age;
}
//定义Person对象对应的字符串表现形式,覆盖Object中的toString方法
public String toString()
{
return "Person[name="+name+",age="+age+"]";
}
}
class ExceptionDemo8
{
public static void main(String[] args)
{
try
{
Person p=new Person("xiaoming",-12);
System.out.println(p);
}
catch(NoAgeException e)
{
System.out.println("异常了");
}
System.out.println("over");
}
}
需求:有一些特定的代码块无论异常是否发生,都需要执行。
因为异常会引发程序跳转,导致有些语句执行不到,无法满足这个需求。
异常捕获处理时java提供解决方案。
try catch finally
finally 就是解决这个问题,这个代码块中存放的代码都是一定会被执行的。
应用场景:
定义一个功能往数据库中添加数据。
void add(Data data)
{
1,连接数据库。
try{
2,添加数据。//添加数据时发生了异常情况,throw new SQLException();程序跳转。
//而断开连接必须要执行,应为不执行,连接资源在浪费。
//无论是否发生问题,都要执行断开连接的动作,从而释放资源。
}catch(SQLException)
{
//解决数据库的问题。
//同时将问题告诉调用者。
throw new NoAddException();
}
finally{
3,断开连接。
}
}
总结:
finally的使用场景.
只要程序中使用了具体的资源(数据库资源,IO资源,网络连接socket等)需要释放,都必须定义在finally中,你在定义程序,只要问题发生与否,指定程序都需要执行时,就定义在finally中。
class NoShowException extends Exception
{
NoShowException(String message)
{
super(message);
}
}
class Demo
{
void show(int num)throws NoShowException
{
if(num<0)
throw new Exception(num+",数值非法");
System.out.println("show..."+num);
}
}
class ExceptionDemo9
{
public static void main(String[] args)
{
Demo d=new Demo();
try{
d.show(5);
}catch(NoShowException e)
{
System.out.println(e.toString());//打印的是异常名称+异常信息。
//如果异常发生,处理完毕后,希望功能结束。
//return;
//注意:有一种情况发生,finally也不会执行。
System.exit(0); //退出jvm.
}
finally
{
System.out.println("hello...");
}
System.out.println("over");
}
Test t=new Test();
int num=t.show(-4);
System.out.println("num"+num);
}
class Test
{
int show()
{
try{
if(true)
throw new Exception() ;
return 4;
}catch(Exception e)
{
System.out.println(e.toString);
return 200;
}
finally{
System.out.println("finally ...");
return 100;
}
}
}
try catch finally 组合方式
:
1,
try catch
:对代码进行异常检测,并对检测的异常传递给catch处理。
异常捕获处理。
void show()//不需要throws
{
try{
throw new Exception();
}catch(Exception e){
}
}
2,
try finally
:对代码进行异常检测,检测到异常后因为没有catch,所以一样会被默认jvm抛出。
异常是没有捕获处理的。但是功能所开启的资源需要进行关闭,所有finally,只为关闭资源。
void show()//需要throws
{
try{
throw new Exception();
}finally{
}
}
3,
try catch finally
检测异常,并传递给catch处理,并定于释放资源。
4,
try catch1 catch2 catch3
……
异常在继承或者实现中使用细节
:
1,子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类,或者不声明。
2,当父类方法声明多个异常,子类覆盖时只能声明多个异常的子集。
3,当覆盖的方法没有异常声明时,子类覆盖时是无法声明异常的。
举例:父类存在这种情况,接口也有这种情况,
问题:接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?
无进行throws声明,只能catch的捕获,万一问题处理不来呢?
catch中继续throw抛出,但是,只能将异常转换成RuntimeException子类抛出。
Interface Inter
{
public void show();
}
class Demo implement Inter
{
public void show()
{
try{
throw new Exception();
}catch(Exception e)
{
code...;
throw new RuntimeException("");//告知调用者,问题所在。
}
}
}
Test1
/*
案例一:
描述一个长方形。
*/
class NoValueException extends RuntimeException
{
NoValueException()
{
super();
}
NoValueException(String message)
{
super(message);
}
}
class Rec
{
private int length;
private int width;
Rec(int length ,int width)
{
if(length<=0||width<=0)
{
throw new NoValueException("wrong value");
}
this.length=length;
this.width=width;
}
public int getArea()
{
return length*width;
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Rec r=new Rec(-3,4);
int area=r.getArea();
System.out.println("area="+area);
}
}
Test2
/*
案例二:毕老师用电脑讲课。
两个对象:
老师:
属性:name
行为:讲课
电脑:
行为:运行。
考虑问题。
1,电脑蓝屏-->异常
2,电脑冒烟-->异常
*/
//可以声明,让调用者给出处理方式。
class LpException extends Exception
{
LpException()
{
super();
}
LpException(String message)
{
super(message);
}
}
class MyException extends Exception
{
MyException()
{
super();
}
MyException(String message)
{
super(message);
}
}
/*
讲课中冒烟,问题可以临时解决,是冒烟问题没有直接处理,所以就使用throws声明。
但是发现,这个问题不应该是属于讲课的问题,调用讲课方法的调用者是处理不了这个冒烟问题的。
该调用者能处理的应该是冒烟导致的课程进行不下去的问题。
应该在列出一个异常,课时停止异常。
*/
class NoPlanException extends Exception
{
NoPlanException()
{
super();
}
NoPlanException(String message)
{
super(message);
}
}
class Notebook
{
private int state=2;
public void run()throws LpException,MyException//方法上可以通过throws声明多个异常
{
if(state==1)
throw new LpException("电脑蓝屏了");
if(state==2)
throw new MyException("电脑冒烟了");
System.out.println("笔记本电脑运行");
}
public void reset()
{
state =0;
System.out.println("重启");
}
}
class Teacher
{
private String name;
private Notebook book;
Teacher(String name)
{
this.name=name;
book =new Notebook();
}
//讲课
public void prelect()throws NoPlanException
{
/*
调用到了声明异常的方法,在这里到底是捕获好,还是声明好?
有具体的捕获处理方式?有,就捕获,没有,那么就声明。
可以处理,重启即可。
*/
try
{
book.run();
}
catch(LpException e)//LpException e=new LpException("电脑蓝屏了");
{
//重启
System.out.println(e.toString());//异常的名称+异常的信息。
//e.printStackTrace();
book.reset();
}
catch(MyException e)
{
System.out.println(e.toString());
test();
//冒烟问题没有解决,继续声明throws出去。
// throw e;
throw new NoPlanException("课时停止"); //异常转换。
}
System.out.println(name+"讲课");
}
//留练习
public void test()
{
System.out.println("做练习");
}
}
class ExceptionTest2
{
public static void main(String[] args)
{
Teacher t=new Teacher("毕老师");
try
{
t.prelect();
}
catch(NoPlanException e)
{
System.out.println(e.toString());
System.out.println("换老师");
}
System.out.println("over");
}
}
/*
throw和throws的区别?
1,throw用在函数内。
throws用在函数上。
2,throw抛出的是异常的对象。
throws是用于进行异常类的声明,后面异常类可以有多个,用逗号隔开。
*/