面向对象
首先什么都不说,Java是面向对象的语言,当然也对异常包装成了对象(Throwable),方便了我们的使用,这个是必须的,咱们以后天天面向对象,当然现在我是没有对象的,所谓对象就是女朋友。准备就业后才去new 一个对象。
异常:就是程序在运行时出现不正常情况。
异常由来:
问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述。并封装成对象。
其实就是Java对不正常情况进行描述后的对象体现。描述不正常的情况的类,就称为异常类。
不同的问题用不同的类进行具体的描述。比如角标越界异常,空指针异常等等。
问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系。
异常的好处:
1)将问题进行封装。
2)将正常流程代码和问题处理代码相分离,方便于阅读。
异常的体系
异常体系的特点:子类的后缀名都是用父类名作为后缀,阅读性很强。
最终问题(Final 问题(当然放几个洋屁嘛!当然不是放的很响))(不正常情况)就分成了两大类。
一种是Error,一种是Exception。由于发现这俩大类中还有很多共性东西,所以又进行了抽取。抽取成了Throwable。
Throwable:
无论是Error,还是Exception。
问题,问题发生就应该可以抛出,让调用者知道并处理。
该体系的特点就在于Throwable及其所有的子类都具有可抛性。
可抛性到底指的是什么呢?throws throw
怎么体现可抛性呢?凡是可以被这两个关键字所操作的类和对象都具备可抛性。
1、一般不可处理的。Error
特点:是同Jvm抛出的严重性的问题。
通常出现重大问题如:运行的类不在在或者内存溢出等。
这种问题发生一般不针对性处理。直接修改程序。
错误:通常是不编写代码对其进行针对处理。
2、可以处理的。Exception
处理Exception异常有俩种方法:
一是:在运行时运行出现的一些情况,可以通过try catch finally
二是:通过throws在函数上声明。(如果函数内throw异常,那么函数就必须声明)
异常:是在运行时期发生的不正常情况..
处理异常(Exception)的几种格式:
格式一:
try{
可能发生异常的代码;
}
catch(接收捕获异常类的对象 异常类引用变量){
处理代码;
}
格式二:
try{
可能发生异常的代码;
}
fianlly{
一定要执行的语句;(通常用于关闭资源)
}
格式三:
try{
可能发生异常的代码;
}
catch(捕获异常类的对象 异常类引用变量){
处理代码;
}
finally{
一定要执行的语句;(通常用于关闭资源)
}
异常示例:
public class Test {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9};
try{
System.out.println(arr[5]);
}
catch (Exception e){
System.out.println("数组角标越界");
}
finally{
System.out.println("return 之前必须执行");
return ;
}
}
}
throw和throws的用法
1)throw定义在函数内,用于抛出异常对象。
2)throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。
异常的选用:
当抛出的异常是调用者误操作完成的,那么继承RuntimeException类,如果该异常的发生,就让该程序停掉。
一般情况下继承的都是Exception异常。
异常有两种:
编译时异常:该异常在编译时,没有处理(没有抛出也没有try),编译失败。
运行时异常(编译时不检测):
在编译时,不需要处理,编译器不检查。
该异常的发生,建议不处理,让程序停止,需要调用者对程序进行修正。
注意:
当函数内有throw抛出异常对象,并未进行try处理。必须要在函数上声明,否则编译失败
Exception有一个特殊的子类,RuntiimeException。
也就是说,函数内如果抛出的RuntimeException异常,函数上可以不用声明。
例子:
class MyRutimeException extends RuntimeException{
MyRutimeException(String msg){
super(msg);
}
}
public class Test{
public static void main(String[] args) {
try{
show(true);
}
catch(MyRuntimeException e){
show(false);
}
}
public static voidshow(boolean b){
if(b)
System.out.println("程序正常运行");throw new MyRutimeException("值为true了");
}
}
当然上面的程序是有问题,因为继承RuntimeException是不需要对代码进行针对性的处理的,因为运行时是调用者误操作出现异常了,就要让调用者进行处理了,只是我是这个程序的调用者,我才对程序进行了try处理。
注意:
如果Exception在内部被解决,比如内部catch(捕获)并对异常进行了处理,则该方法不用声明异常(也就是throws)。
记住一点,catch是用于处理异常,如果没有catch就代表异常没有被处理。
如果该异常是检测,可以没有catch语句。(比如,try finally)
当然:如果抛出的是多个异常,就要有几个catch,也有简单的做法,直接抛父类异常Exception。
异常处理的原则:
1、函数内容如果抛出需要检测的异常,那么函数上必须要声明。
否则必须在函数内用try catch捕捉,否则编译失败。
2、如果调用到了声明异常的函数,要么try catch要么throws,否则编译失败。
(如果调用的函数抛出异常,那么调用者可以try catch也可以throws)
(如果调用的是复写父类的函数抛出异常,那么调用者只能抛出父类同样的异常,或者父类异常的子集)
3、什么时候catch,什么时候throws 呢?
功能内容可以解决,用catch。
解决不了,用throws把异常告诉调用者,由调用者解决 。
4、一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。
内部有几个需要检测的异常,就抛几个异常,抛出几个,就对应有几个catch。
5、抛到最后抛给谁才是个头呢?答案是抛到虚拟机才是头。
finally特殊之处
finally语句中定义的是一定要执行的语句,通常用于关闭资源。(因为资源必须释放)
finally除非遇到System.exit(0);也是就虚拟机退出才不会执行。
子类覆盖父类时对异常处理的注意事项:
1、如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2、如果父类方法抛出多个异常,那么子类再覆盖该方法时,只能抛出父类异常的子集。
(也就是父类如果抛多个异常,那么我们不能直接抛Exception异常。)
(如果抛一个异常的时候,是可以抛父类异常的)
3、如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
子类方法发生了异常,就必须要进行try处理,绝对不能抛。
4、如果在子类的覆盖方法确实需要抛出非父类的异常或者该异常的子类,则必须在内部解决。
自定义异常
定义类继承Exception或者RuntimeException
(其实我们这样也是用面向对象思考问题,把我们程序设计中可能出现的问题进行了封装。)
1)为了让该自定义类具备可抛性
2)让该类具备操作异常的共性方法
3)当要自定义异常的信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。
例子:
class MyException extends Exception{
MyException(String message){
super(message);//父类带参数的构造函数
}
}
/*
在我们的程序中,数组为空就会发生空指针异常。
*/
public class Test {
public static void main(String[] args)/*throws Exception ...当然这里也可以直接把问题抛出去*/{
//定义了一个空数组。
int[] arr =new int[0];
try{
/*对调用的方法进行try catch处理。*/
method(arr);
}
catch(Exception e){
/*跟踪打印下抛异常的类和问题。*/
System.out.println(e.toString());//MyException: 空指针异常
}
}
/*定义一个方法,打印一下数组,对数组进行空指针判断,如果为空发生异常。*/
public static void method(int[] arr)throws Exception{
if(arr.length==0)
/*如果数组为空,就在函数内抛出异常,在函数上声明抛出的异常。*/
throw new MyException("空指针异常");
System.out.print("[");
for(int x=0;x<arr.length;x++){
}System.out.print(arr[x]);
}
System.out.println("]");
}
注意:当捕获到的异常,本功能处理不了时,可以继续在catch中抛出,让调用者去处理。
public static void method(){
try{
异常代码;
}
catch(MyException e){
throw new MyException("MyException");
}
}
异常应用示例:
/*
毕老师用电脑上课。
问题领域中涉及两个对象。
毕老师,电脑。
分析其中的问题。
比如电脑蓝屏啦。冒烟啦。
*/
/*对电脑蓝屏故障进行描述*/
import java.util.Random;
class LanPingException extends Exception {
LanPingException(String msg) {
super(msg);
}
}
/*对电脑冒烟故障进行描述*/
class MaoYanException extends Exception {
MaoYanException(String msg) {
super(msg);
}
}
/*对课程出现问题进行描述*/
class NoPlanException extends Exception {
NoPlanException(String msg) {
super(msg);
}
}
/*
对电脑进行描述
一个是电脑的运行方法 run()
一个是电脑的重启方法 reset()
*/
class Computer {
private int state = new Random().nextInt(3);
public void run() throws LanPingException,MaoYanException{
if(state==1)
throw new LanPingException("电脑蓝屏啦!!");
if(state==2)
throw new MaoYanException("电脑冒烟啦!!");
System.out.println("电脑运行");
}
public void reset() {
state= 0;
System.out.println("电脑重启");
}
}
/*
对教师进行描述
教师属性 姓名(name),电脑(comp)
教师行为 讲课(prelect()) 如果出现异常,教师还可以布置练习方法(Test())
*/
class Teacher {
private String name;
private Computer comp;
Teacher(String name) {
this.name= name;
comp = new Computer();
}
public void prelect() throws NoPlanException {
try{
comp.run();
System.out.println(name+"老师讲课");
}
catch(LanPingException e) {
System.out.println(e.toString());
comp.reset();
prelect();
}
catch(MaoYanException e) {
System.out.println(e.toString());
test();
/*
对异常进行了转换,本人是电脑冒烟故障,但是会影响到教师的课程,所以又对异常进行了封装,把电脑的问题转换成教材课程无法继续的问题,这就是异常的异常转换。
*/
throw new NoPlanException("课时进度无法完成,原因:"+e.getMessage());
}
}
public void test() {
System.out.println("大家练习");
}
}
/*
创建教师对象,调用教师的讲课方法。
教师讲课可能出现问题,所以对发生的问题进行了处理
*/
public class ExceptionTest {
public static void main(String[] args) {
Teacher t = new Teacher("毕老师");
try {
t.prelect();
}
catch(NoPlanException e) {
System.out.println(e.toString());
System.out.println("换人或者放假");
}
}
}
异常转换:
如果该异常处理不了,但并不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常,或者异常可以在内部处理,但需要将异常缠上本功能相关的内容提供出去,调用者知道,并处理,也可以将捕获的异常处理后,转换为新的异常。
/*今天就到这里了,明天对异常的代码进行具体的补充,明天的博客会写到多线程这一块,甚至集合,就这样先睡觉了*/
异常——练习
/*
有一个圆形和长方形,都可以获取面积,对于面积如果出现非法的数值,视为是获取面积出现问题。
问题通过异常通过异常来表示。
现有对这个程序进行基本设计。
*/
/*
由于面向对象的分析,分析出对圆和长方形求出对应的面积。
由于都是求面积,所以把它们的共性向上抽取,抽成一个接口;
方便让程序员们一看就知道这个体系是在求面积。
*/
interface Shape{
/*
分析返回结果,为了求面积,我们只要知道面积,需不需要给我返回结果。
单纯需要知道面积,而不需要面积再去做运算,是不需要返回结果的。void
*/
void getArea();
}
class NoValueRuntimeException implements RuntimeException{
NoValueException(String msg){
this.msg=msg;
}
}
class Circle implements Shape{
private double radius;
private static finaldouble PI = 3.14;
Circle(double radius){
if(radius <=0)
throw new NoValueRuntimeException("圆出先非法值");
this.radius=radius;
}
/*复写父接口的方法,定义子接口,特定内容。父类接口方法的默认权限就是public*/
public void getArea(){
//圆形的面积计算公式:π*r2
System.out.println(PI*radius*radius);
}
}
class Rec implements Shape{
private double len;
private double wid;
Rec(double len,double wid){
if(len<=0 || wid<=0)
throw new NoValueRuntimeException("正方形出先非法值");
this.len=len;
this.wid=wid;
}
public void getArea(){
//长方形的面积计算公式:len(长)*wid(宽)
System.out.println(len*wid);
}
}
注意一点:我也是今天才想起来的,就是但写的程序编译的时候报异常了,那么只要把异常处理掉,就可以了。