黑马程序员----多态和内部类、异常

来源:互联网 发布:淘宝提醒发货不见了 编辑:程序博客网 时间:2024/05/22 02:07

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------



一、面向对象三大特征之多态

什么叫做多态?

行为上:父类引用指向子类对象

表现上:同一个父类的不同子类在接到同一个消息时,做出的响应不同

例如:

Animal a=new Cat();
我们都知道,猫类肯定是继承于动物类的,那么为什么一个动物类的引用可以指向一个猫类呢?

其实这个叫做类型提升,也叫向上转型

既然有向上,那么就有向下咯,接上面:

Cat c=(Cat)a;
我们发现虽然引用在变来变去,但是本质一直都是一个猫对象,这是不变的。

多态的前提:

从上面代码我们可以看到,猫是继承于动物类的,所以动物引用可以指向猫,那么当两个类不是继承关系的时候可以实现多态吗?答案是:NO,大家可以想一个问题,为什么动物类引用可以指向猫东西呢?
观察一下代码,思考问题:
abstract class Animal{String name;int age;int legs;abstract void eat();abstract void sleep();}class Cat extends Animal{void eat(){s.o.p("吃鱼");}void sleep(){s.o.p("趴着睡");}}main(){Animal a=new Cat();}
虽然我们创建的是一个猫对象,但是由于这个猫类是继承于动物类的,因此,猫类中包含所有动物类的属性和方法,也就是说猫类就是个加强版的动物类,那么我们当然也是可以把猫当成是动物来看的,也就可以用动物引用指向猫对象
而如果两个类没有继承关系,例如一下代码:
Student s=new Cat();
我们知道学生类与猫类没有继承关系,那么我们就不能把一个猫看成一个学生,因此不能使用学生类引用指向猫对象。
这里有个小窍门:当你不确定能不能用类A的引用指向类B实现多态时,心里默念一下语句:B也是一个A,如果这句话是成立的,那表示是可以实现多态的

多态的好处:

大大提高程序扩展性,操作的是父类(也就是子类中共性的部分,既然所有子类都有,所以可以一起操作)。例如以下代码:
class Animal{abstract void eat():abstract void sleep():}class Cat extends Animal{void eat(){s.o.p("吃鱼");}void sleep(){s.o.p("趴着睡");}}class Bird extends Animal{void eat(){s.o.p("吃虫子");}void sleep(){s.o.p("站着睡");}}void f(Animal a){a.eat();a.sleep();}f(new Cat());f(new Bird());
可以看到,由于f方法内操作的都是父类的方法,因此我们将参数类型设为动物类,因此我们可以传入任何动物类的子类,由于eat和sleep为动物类的方法,因此子类一定也由此方法,因此可以这样使用。我们一个方法就实现了对所有动物子类对象的操作,而不用去为了每一个子类单独写一个方法

多态的局限性:

只能使用父类的引用访问父类的成员。如果一定要使用某个子类的方法,可以使用instanceof方法来判断。通常不这样使用,因为一般设计该方法的目的就是操作父类,也就是共性部分。

多态应用举例:

1.主板示例(主板与各硬件间的配合使用问题)
可以在主板中为每个硬件焊个专用的接口供其使用,但是耦合性太强,扩展性太差,不适合,所以我们设计一个PCI接口,只要主板符合该接口规则(主板类内部将此接口作为参数使用),硬件符合该接口规则(实现该PCI接口),那么主板就能用一个卡槽(方法)使用所有符合该PCI接口规则的硬件(PCI的子类们),大大提供扩展性
代码如下:
/*电脑主板示例:电脑为main函数电脑的运行依赖于主板的运行*//*class MainBoard{public void run(){System.out.println("mainboard run");}public void stop(){System.out.println("mainboard stop");}public void userNetCard(NetCard c){if(c==null)return;c.open();//..........c.close();}}//某天想上网了,买了个网卡,网卡需要插到主板上运行(在主板上添加使用网卡方法)class NetCard{public void open(){System.out.println("netcard open");}public void close(){System.out.println("netcard close");}}*///某天又想听音乐了,买个声卡,同样需要插到主板上运行(添加使用声卡方法)//但是这样一来,主板的扩展性太差,也就是说各种硬件与主板的耦合性太强,互相太依赖//MainBoard如果要使用userNetCard,依赖于NetCard类,同样类NetCard类的运行依赖于MainBoard类//降低这种耦合性:使用多态(1.抽象父类,2.接口,其实本质上是同样的道理,子类在继承(实现)这二者的同时,实现其中方法)//此处使用接口实现多态interface PCI//国际通用的卡槽{public abstract void open();public abstract void run();public abstract void close();}class MainBoard{public void run(){System.out.println("mainboard run");}public void stop(){System.out.println("mainboard stop");}public void userPCI(PCI p)//相当于PCI p = new NetCard(); PCI p=new SoundCard();{if(p==null){System.out.println("此卡槽暂无硬件连接");return;}p.open();p.run();p.close();}}//网卡,实现了PCI接口class NetCard implements PCI{public void open(){System.out.println("netcard open");}public void run(){System.out.println("netcard run");}public void close(){System.out.println("netcard close");}}//声卡,实现了PCI接口class SoundCard implements PCI{public void open(){System.out.println("soundcard open");}public void run(){System.out.println("soundcard run");}public void close(){System.out.println("soundcard close");}}class InterfaceDuoTaiDemo {public static void main(String[] args) {MainBoard mb = new MainBoard();//买了个主板焊到电脑上,通电运行,其实此处也可以传入一个主板父类或者主板接口,提高扩展性mb.run();//运行期间//mb.userNetCard(new NetCard());此种用法扩展性太差,每增加一个硬件都要在主板上焊出来一个专用的卡槽使用mb.userPCI(null);//卡槽无硬件连接mb.userPCI(new NetCard());//卡槽插入一个网卡mb.userPCI(new SoundCard());//卡槽插入一个声卡//无论什么卡,只要符合主板上的接口规则(实现了PCI接口的子类),都能被主板使用(作为主板方法参数)mb.stop();}}
运行图:


我们可以看到,不管我们如果增加卡的种类,只要该卡实现了PCI接口,那么我们就可以利用多态的特性,通过一个方法来调用这些不同的卡

这就是多态的威力。

多态中成员的特点:

非静态成员函数特点:

1.编译时期:参阅引用型变量所属类(父类)是否有对应的方法如果有,编译通过,如果没有,失败,这就是为什么只能使用父类方法,因为使用子类特有的方法,编译期会报错。

2.运行时期:参阅对象所属类(子类)是否有对应的方法如果有,编译通过,如果没有,失败

成员变量的特点:

无论编译运行,都参考引用型变量所属的类(父类),不管父类中变量名是否同子类同名,都在堆中有自己的空间,不存在覆盖问题

静态成员的特点

无论编译运行,都参考引用型变量所属的类(父类)

本质原因:

静态成员(静态绑定)->保存在共享区的静态部分成员跟类名绑定在一起(Fu f=new Zi();f.static();相当于Fu.static();)

非静态方法(动态绑定)->保存在共享区的非静态部分方法跟对象绑定(Fu f=new Zi();f.yiban();相当于new Zi().yiban();)


所有类直接或间接父类:Object

因此,当要操作的子类不确定时,可以使用Object来接收,因为不管是哪个类,都是Object的子类,都能使用多态。

Object代码举例:

/*每个类都直接间接的继承于Object类也就有Object中的功能*/class Demo//默认每个类都直接或者间接继承于Object{public int num;Demo(int n){num=n;}public boolean equals(Object obj){//1.传入的是否为Demo类型if(!(obj instanceof Demo))return false;return this.num==((Demo)obj).num;}}class Person{}class ObjectDemo {public static void main(String[] args) {/*Demo d1=new Demo();Demo d2=new Demo();Demo d3=d1;System.out.println(d1.equals(d2));//不等System.out.println(d1.equals(d3));//相等*/Demo d1=new Demo(1);Demo d2=new Demo(2);Demo d3=new Demo(1);System.out.println(d1.equals(d2));System.out.println(d1.equals(d3));//不是同一个对象,也能相等,比较的方式变了System.out.println(d1.equals(new Person()));//不是同一个类的对象也能比,程序健壮性好System.out.println(((d1.getClass()).toString()).substring(6)+"@"+Integer.toHexString(d1.hashCode()));Class c=d1.getClass();System.out.println(c.getName()+"@"+Integer.toHexString(d1.hashCode()));System.out.println(d1.toString());}}
运行图:



多态小练习:

代码如下:

/*数据库操作示例:操作数据库的方法很多:JDBC,Hibernate等但操作的内容等是相同的:C-》create R-》read U-》update D-》delete因此可以说JDBC,Hibernate符合这种操作的规则,不同的是各自的实现细节此处可以使用接口解决*/interface UserInfoByDao//Dao->date access object数据访问对象,访问数据层的对象{public abstract void connect(String connStr);public abstract void add(String user);public abstract void delete(String user);public abstract void update(String user);public abstract void select(String user);public abstract void shutdown();}//JDBCclass UserInfoByJDBC implements UserInfoByDao{public void connect(String connStr){System.out.println("JDBC连接数据库:"+connStr);}public void shutdown(){System.out.println("JDBC关闭数据库");}public void add(String user){connect("数据库MongGoDB");System.out.println("JDBC增加数据:"+user);shutdown();}public void delete(String user){connect("数据库MongGoDB");System.out.println("JDBC删除数据:"+user);shutdown();}public void update(String user){connect("数据库MongGoDB");System.out.println("JDBC修改数据:"+user);shutdown();}public void select(String user){connect("数据库MongGoDB");System.out.println("JDBC查询数据:"+user);shutdown();}}//Hibernateclass UserInfoByHibernate implements UserInfoByDao{public void connect(String connStr){System.out.println("Hibernate连接数据库:"+connStr);}public void shutdown(){System.out.println("Hibernate关闭数据库");}public void add(String user){connect("数据库MongGoDB");System.out.println("Hibernate增加数据:"+user);shutdown();}public void delete(String user){connect("数据库MongGoDB");System.out.println("Hibernate删除数据:"+user);shutdown();}public void update(String user){connect("数据库MongGoDB");System.out.println("Hibernate修改数据:"+user);shutdown();}public void select(String user){connect("数据库MongGoDB");System.out.println("Hibernate查询数据:"+user);shutdown();}}class InterfaceDuoTaiDemo2 //使用数据库访问类的类{public static void main(String[] args) {UserInfoByDao ui = new UserInfoByJDBC();ui.add("helong");ui.delete("ldy");ui.update("hexiaolong");ui.select("lfy");ui = new UserInfoByHibernate();ui.add("helong2");ui.delete("ldy2");ui.update("hexiaolong2");ui.select("lfy2");}}

运行图:



多态总结:

使用多态,可以大大的提高程序的扩展性,就好像例子和练习中的一样,我们不可能为了操作每个子类都去写一个方法什么的,最关键的是我们调用的子类方法一般都是一样的几个方法,唯一不同就是各个子类的实现不同,因此我们一般选择用多态来解决问题,这样只需写一个操作父类的方法就OK了。



二、内部类

什么是内部类?

从名字我们看以看出这个类一定是定义在什么里面的,所以叫内部嘛,那么到底是定义在什么里呢?答案是另一个类中。也就是说类A中定义了类B,那么类B就是个内部类

那么这个类B在类A中是个什么身份呢?因为我们知道类中定义的要么是成员变量,要么是成员方法,其实这个类B在类A中也是作为一个成员存在的,很多时候跟成员方法非常类似。那么我们就要考虑一个问题了,既然类B做为一个成员,那么是不是这个类B可以被修饰成员的关键字修饰呢?答案是YES,还真的可以。我们知道Java中类是不能被private修饰的,因为一般这样修饰的类就没意义了,但是如果一个类作为内部类呢?这时就可以被private修饰了,因此要是别人问你Java有没有私有的类,你可要注意啦,十有八九就是拿内部类阴你呢。


内部类访问规则:

a.内部类可以直接访问外部类的成员,包括私有
原因:内部类持有外部类的引用:外部类名.this
b.外部类如果想访问内部类,必须建立内部类对象
c.如果内部类需要使用static修饰符,那么该内部类必须为静态的
 如果外部类静态方法要访问内部类,那么内部类也必须为静态的
d.内部类被static修饰,只能访问外部类的静态成员
new Outer.Inner().function();//调用静态内部类的非静态方法

使用内部类及访问规则:

/*内部类访问规则:1.内部类可以直接访问外部类成员,包括私有2.外部类访问内部类需要建立内部类对象*/class Outer{static{System.out.println("外部类静态代码块");}int x=1;private class Inner{//static,普通内部类不允许使用static修饰符{System.out.println("内部类构造代码块");}int x=2;public void getX(){int x=3;System.out.println("Inner:"+x);//1.x  2.this.x  3.Outer.this.x//虚拟机默认的查找标示符顺序:1.当前作用域,2.同类作用域(相当于this.x),3.外部类作用域(相当于Outer.this.x)}}public void method(){Inner in = new Inner();in.getX();}}class InnerDemo {public static void main(String[] args) {Outer out = new Outer();out.method();//Outer.Inner in = new Outer().new Inner();//当内部类定义在外部类成员位置,且不为私有//in.getX();}}
运行图:



内部类定义原则:

描述事物时,事物内部还有事物,用内部类表示,因为内部事物在使用外部事物的某部分

例如;Body为外部类(私有:血液,公有:运动),Heart为内部类(需要访问Body的血液),如果Heart不为内部类,就访问不到Body的私有成员,同时使用内部类更能体现Body和Heart的关系。

内部类最好定义为private,在外部类中提供方法访问它,只有定义在成员位置的内部类作为成员被private,static等修饰


特殊:内部类定义在局部时

特点:
1.不可以被修饰符修饰
2.可以直接访问外部类成员,因为还持有Outer.this引用,但是不可以访问它所在方法的局部变量,只能访问被final修饰的局部变量
对于这个问题,在JDK1.8中,大家可能会发现即便没被final修饰的局部变量,依然可以在内部类中被访问,这是因为在JDK1.8中,此处的局部变量被默认加上了final修饰
因此不能访问被final修饰的这个特点还是存在的,只是表面看起来好像可以访问非final变量了而已。
那么为什么不能访问未被final修饰的变量呢?
原因:内部类也是类,存储在堆中,而局部变量存储在栈中,生命周期不同,如果使用final修饰变量那变量也被存储在堆中,就可以访问了,因此说白了,就是生命周期的问题

匿名内部类:

特点:

a.就是内部类的简写

b.定义匿名内部类的前提:内部类必须是继承一个类或者实现接口,由于每个类都会直间接继承于object,所有总是可以定义匿名内部类

c.匿名内部类的格式:new 父类或者接口(){定义子类的内容,包括实现父类,接口抽象方法}.方法()

d.匿名内部类就是一个匿名子类对象,将封装和调用集合在一起

e.匿名内部类中实现的方法最好不要超过3个,否则阅读性非常差

f.面试题:写出一个没有显式继承也没有接口的匿名内部类

new Object(){定义方法或者复写Object类中的方法}.function();

此匿名类虽然没有显式继承于哪个类,也没有实现接口,但是它继承于Object,所以可以这么写

g.链式编程:因为匿名内部类的使用特点,如果其内部不止一个方法,那么使用链式书写更清晰,让方法返回this


匿名内部类练习:

/*写出一个没有继承,也没实现接口的匿名内部类使用*/interface Lian//测试匿名内部类的实现链式编程{public abstract Lian test1();public abstract Lian test2();public abstract Lian test3();}interface Inter{public abstract void method();}class Test{public static Inter function(){return new Inter(){public void method(){System.out.println("Inter method test");}};}}class Outer{int num=3;class Inner{int num=4;public void getNum(){int num=5;System.out.println(num);//1.num   2.this.num   3.Outer.this.num查找num的顺序,由近到远}}static class Inner2{public static void show(){System.out.println("this is inner2 static function");}}private class Heart{public void run(){System.out.println("my heart is runing");}}public void function(){Inner in = new Inner();in.getNum();}public static void show(){System.out.println("this is outer static function");}public void accessHeart(){Heart h=new Heart();h.run();}void test(){int ss=19;class JuBuClass//定义在test方法内,依然可以访问到ss{void show(){System.out.println(ss);//为何这里可以访问到方法的局部变量}}new JuBuClass().show();}}class InnerClassTest {public static void main(String[] args) {Outer out = new Outer();out.function();//在外部类方法中访问内部类Outer.Inner in = new Outer().new Inner();//直接使用内部类的格式in.getNum();Outer.show();Outer.Inner2.show();//Outer.Heart ht=new Outer().new Heart();//报错,heart为私有成员,不能这样访问,必须在外部类中提供访问方法//ht.run();Outer out2=new Outer();out2.accessHeart();//在外部类中定义一个方法来访问私有内部类out2.test();Test.function().method();new Object(){public void show(){System.out.println("没有显式继承实现的匿名内部类使用");}}.show();//链式编程,是可以的。test1().test2().test3();相当于匿名子对象.test1();+匿名子对象.test2();+匿名子对象.test3();new Lian(){int x=0;public Lian test1(){System.out.println("this is test1: "+(++x));return this;}public Lian test2(){System.out.println("this is test2: "+(++x));return this;}public Lian test3(){System.out.println("this is test3: "+(++x));return this;}}.test1().test2().test3();}}

运行图:



内部类总结:

内部类的使用非常广泛,因为我们发现对于很多事物,内部都有较为复杂的部分,那么那一部分只是使用一个方法是无法解决的,即便强行使用一个方法解决了,那么也肯定会违背单一职责原则,也就是方法的职责过多。因此,一般这种时候我们都会使用内部类来描述复杂的部分。

当然匿名内部类的使用也很多,主要是简化了代码的书写,在某些特定场合是非常实用的。



三、异常

什么是异常?

简单说就是程序运行期间出现的不正常的情况

异常的定义:

严重错误:Error(不可治愈疾病):一般不写针对性代码进行处理
非严重错误:Exception(可治愈疾病):一般可以通过某种方式处理
体系:
Throwable:
|--Error
|--Exception


异常处理语句:

既然异常是一个问题,那么我们就应该去解决问题:
try{需要检测的代码}  catch(异常类 名){处理方式}   finally{一定要执行的语句,通常是释放资源}


异常对象常用方法:

a.String getMessage();//返回异常信息

b.String toString();//返回异常名称,信息

c.void printStackTrace();//打印异常名称,信息,出现位置


抛出异常:

通过throws标示符,标示一个方法可能会有异常,且该异常此方法不处理,而是抛出

方法的调用者必须捕捉或者抛出此异常(目前我们主要是捕捉处理)。

举例:就好像打开面包袋吃面包,老板通过throws标示该面包可能坏了,因此我们将打开面包并吃掉的行为放到try中,如果打开袋子发现真的坏了(异常发生),就跳转到catch中,报告说面包坏了,执行处理方法,扔掉。

注意:在一个没有捕捉而是抛出异常的方法里,出现异常方法就结束


异常小练习:

class Demo{public static int div(int x,int y){return x / y;}}class ExceptionDemo {public static void main(String[] args) {//System.out.println(Demo.div(12,0));//出现异常,jvm默认处理,打印异常信息,出现位置,停止程序运行try{System.out.println(Demo.div(12,0));//如果有异常发生,就会有一个异常对象出现在这里,然后跳转到catch语句//将对象作为参数给e,然后执行处理语句,无异常则程序正常执行}catch (Exception e){System.out.println("除数为0错误!");System.out.println(e.getMessage());System.out.println(e.toString());e.printStackTrace();//此句可以看出,jvm调用的就是这个方法打印异常}System.out.println("program over");}}

运行图:


多异常处理:

首先我们要明确一点:方法可能出现不同的异常,但不会同时抛出,一组try...catch....catch只会有一个catch执行
a.声明异常时,尽可能具体一些,这样处理的时候也明确一些
b.方法声明几个异常,就要有几个catch块
如果多个异常出现继承关系,父类的catch块放在后面(以免处理了本不需要自己处理的子类异常)
c.catch块中不要只是简单的打印,而是要具体的处理
例如可以将异常具体信息,发生位置,时间写到一个硬盘的异常日志文件中。

多异常练习代码:
/*多异常处理:1.异常的声明应该尽量具体,这样在catch中处理才能更加确切,针对性更强。2.声明了几个异常,就有几个异常catch块注意:也许方法中除了声明的异常,还会有别的异常发生!!此时有的人做法是在catch块中最后增加一个catch(Exception e)来处理未知的异常缺陷:1.这种做法类似于隐藏异常,而且在不知道具体异常的情况下也无法很好的处理2.如果有未知异常发生,最好的做法就是让jvm捕获,默认处理,停止程序让我们知道*/class Demo{public static int div(int x,int y)throws ArithmeticException,ArrayIndexOutOfBoundsException,RuntimeException//RuntimeException是ArithmeticException的父类//此时的catch语句,捕捉RuntimeException应该放到ArithmeticException后面{int[] arr=new int[x];System.out.println(arr[4]);return x/y;}}class MoreExceptionDemo {public static void main(String[] args) {try{int result=Demo.div(2,0);System.out.println("result:"+result);}/*catch(Exception e)//编译失败,因为如果有次catch,那么后面的catch永远不会执行到,不建议使用Exception,无针对性{System.out.println(e.toString());}*/catch (ArithmeticException ae){System.out.println(ae.toString());}catch(ArrayIndexOutOfBoundsException e){System.out.println(e.toString());}catch(RuntimeException e)//捕捉更高层次的异常类时,放在catch其子类异常的后面{System.out.println(e.toString());}}}
运行图:


自定义异常:

当我们在写程序时,经常会遇到一些程序中可能会发生的特有的问题,这时如果使用已有的异常类来表示并不够准确,而且没有针对性的解决方法,这时我们可以考虑使用自定义异常来描述这些特有问题。

自定义异常的异常信息:因为父类已经完成了对异常信息的操作,因此自定义类只需把信息传给父类即可,通过构造函数中的super(str);完成

继承于Exception或其子类的原因异常类和异常对象都要被抛出,具有可抛性,是Throwable这个体系中的独有特点,只有这个体系的成员可以被throws和throw

当一个方法内部throw一个异常对象(非RuntimeException)时,要么try,要么抛


throws和throw区别:

throws作用在函数上,而throw作用在函数内。

throws是声明一个方法可能抛出多个异常类。而throw则是抛出一个异常对象。


特殊异常RuntimeException

运行时异常,通常是发生了使程序无法正确有意义的运行下去的时候报的异常。
RuntimeException类的自定义子类在方法内手动抛出时,方法上可以不声明,编译一样可以通过,这是该体系的特点。
RuntimeException类或者它的子类在方法上声明时,调用者可以不try或抛,编译通过
本质原因:通常RuntimeException发生后,程序就无法正常运算下去了,如果声明了该异常,使得调用者处理了它,那么就等于隐藏了此异常,继续运行程序,而正确的做法应该是停止运行,修改代码才对。因此这个异常不用声明是有其目的的,目的就是让这类异常发生时,停止程序,然后修改代码,而不是处理后继续运行
因此:如果自定义异常发生后,同样程序不能正确运算下去了,那么自定义异常应该继承于RuntimeException


异常分类:

a.编译时被检测的异常(要么抛,要么try,能被调用者针对性处理):
在javac编译时发现有手动抛出的非RuntimeException及其子类的异常,例如:Exception。
b.编译时不被检测的异常(不用抛,try,不能被调用者针对性处理,需要修改方法代码):
在javac编译时不需要抛出或者处理此类异常也能通过编译,例如RuntimeException


继承中覆盖时的异常特点

本质:使得原本能够处理父方法中异常的地方,一样可以处理子方法异常
1.父方法抛一个异常,子方法可以抛该异常或其子异常(可以多个)
2.父方法抛多个异常,子方法可以抛这些异常或其子异常(可以多于父方法抛出的)
3.父方法没抛出异常,子方法也不能抛出异常

子方法中出现父方法中没声明的异常或其子异常:子方法必须内部try处理,而不能抛


使用异常的好处:

我们知道,即便不使用异常,我们依然可以使用流程代码来解决问题,但是这样一来就会使的原始代码和问题处理的代码混杂在一起,降低阅读性,同时也不利于程序的维护。而使用异常可以使得正常的代码和问题处理代码想分离,这样阅读性更好,且更易于维护。



异常综合练习:

/*校长调用老师的上课方法,老师的上课方法中调用了电脑的运行方法,此时如果电脑发生可解决异常,那么在老师的上课方法中将其解决,如果发生不可解决问题,那么老师将此异常抛给校长(问题:如果抛电脑的问题给校长,校长也解决不了,所以应该是抛自己的问题给校长,电脑的不可解决问题直接导致老师也发生异常:课时无法完成,将此抛给校长,请求校长换老师,或者换电脑,或者暂时放假)*/class BlueScreenException extends Exception{BlueScreenException(String message){super(message);}}class BoomException extends Exception{BoomException(String message){super(message);}}class NoPlanException extends Exception //表示因为电脑爆炸,导致老师课时无法完成{NoPlanException(String message){super(message);}}class Computer{private int state=1;public void run()throws BlueScreenException,BoomException{if(state==2)throw new BlueScreenException("电脑蓝屏了。。。");if(state==3)throw new BoomException("电脑爆炸了。。。");System.out.println("电脑运行了。。。");}public void setState(int state){if(state!=this.state&&(state==1||state==2||state==3)){this.state=state;}else{System.out.println("电脑状态设置失败。。。");}}public void reset(){state=1;System.out.println("电脑重启了。。。");}}class Teacher{private String name;public Computer cmpt;Teacher(String name){this.name=name;cmpt=new Computer();}public void teach()throws NoPlanException{try{cmpt.run();}catch(BlueScreenException e){System.out.println(e.toString());cmpt.reset();}catch(BoomException e){System.out.println(e.toString());throw new NoPlanException("课时无法完成,原因:"+e.getMessage());}System.out.println("开始上课了。。。");}}class ExceptionTest2 //相当于校长{public static void main(String[] args) {Teacher t = new Teacher("刘老师");try{t.teach();}catch (NoPlanException e){System.out.println("换电脑,或者换老师,或者暂时休假。。。");}t.cmpt.setState(2);try{t.teach();}catch (NoPlanException e){System.out.println("换电脑,或者换老师,或者暂时休假。。。");}t.cmpt.setState(3);try{t.teach();}catch (NoPlanException e){System.out.println("换电脑,或者换老师,或者暂时休假。。。");}}}
运行图:


异常总结:

异常在一定程序上,将Java的面向对象思想体现的淋漓尽致,使用了异常我彻底明白了万物皆对象的思想,就连程序中可能出现的问题这种虚无缥缈的东西都能被封装成对象,而且由于封装成了对象,能够更好的处理异常,因此我们可以知道,将事物封装成对象的重要性。


------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

0 0
原创粉丝点击