黑马程序员----多态和内部类、异常
来源:互联网 发布:淘宝提醒发货不见了 编辑:程序博客网 时间:2024/05/22 02:07
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、面向对象三大特征之多态
什么叫做多态?
行为上:父类引用指向子类对象。
表现上:同一个父类的不同子类在接到同一个消息时,做出的响应不同。
例如:
Animal a=new Cat();我们都知道,猫类肯定是继承于动物类的,那么为什么一个动物类的引用可以指向一个猫类呢?
其实这个叫做类型提升,也叫向上转型。
既然有向上,那么就有向下咯,接上面:
Cat c=(Cat)a;我们发现虽然引用在变来变去,但是本质一直都是一个猫对象,这是不变的。
多态的前提:
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();我们知道学生类与猫类没有继承关系,那么我们就不能把一个猫看成一个学生,因此不能使用学生类引用指向猫对象。
多态的好处:
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.主板示例(主板与各硬件间的配合使用问题)/*电脑主板示例:电脑为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");}}
运行图:
多态总结:
二、内部类
什么是内部类?
从名字我们看以看出这个类一定是定义在什么里面的,所以叫内部嘛,那么到底是定义在什么里呢?答案是另一个类中。也就是说类A中定义了类B,那么类B就是个内部类。
那么这个类B在类A中是个什么身份呢?因为我们知道类中定义的要么是成员变量,要么是成员方法,其实这个类B在类A中也是作为一个成员存在的,很多时候跟成员方法非常类似。那么我们就要考虑一个问题了,既然类B做为一个成员,那么是不是这个类B可以被修饰成员的关键字修饰呢?答案是YES,还真的可以。我们知道Java中类是不能被private修饰的,因为一般这样修饰的类就没意义了,但是如果一个类作为内部类呢?这时就可以被private修饰了,因此要是别人问你Java有没有私有的类,你可要注意啦,十有八九就是拿内部类阴你呢。
内部类访问规则:
使用内部类及访问规则:
/*内部类访问规则: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等修饰。
特殊:内部类定义在局部时
匿名内部类:
特点:
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();}}
运行图:
内部类总结:
内部类的使用非常广泛,因为我们发现对于很多事物,内部都有较为复杂的部分,那么那一部分只是使用一个方法是无法解决的,即便强行使用一个方法解决了,那么也肯定会违背单一职责原则,也就是方法的职责过多。因此,一般这种时候我们都会使用内部类来描述复杂的部分。
当然匿名内部类的使用也很多,主要是简化了代码的书写,在某些特定场合是非常实用的。
三、异常
什么是异常?
异常的定义:
异常处理语句:
异常对象常用方法:
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");}}
运行图:
多异常处理:
/*多异常处理: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:
异常分类:
继承中覆盖时的异常特点:
子方法中出现父方法中没声明的异常或其子异常:子方法必须内部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培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
- 黑马程序员----多态和内部类、异常
- 黑马程序员__内部类和异常
- 黑马程序员-内部类和异常处理
- 黑马程序员_day011_内部类和异常
- 黑马程序员__内部类和异常
- 黑马程序员:多态和内部类
- 黑马程序员--java基础--对象的多态、内部类、匿名内部类、异常处理机制
- 黑马程序员:java学习要点-内部类和异常
- 黑马程序员---面向对象:内部类、异常和包
- 黑马程序员-面向对象(下)--内部类和异常
- 黑马程序员----String字符串、内部类和异常
- 黑马程序员 学习总结之异常和内部类
- 黑马程序员学习(六) 内部类和异常机制
- 黑马程序员_多态和内部类
- 黑马程序员_Java多态和内部类
- 黑马程序员--Java学习14--多态和内部类
- 黑马程序员--java--多态和内部类
- 黑马程序员—内部类与匿名内部类 异常
- WIFI基本知识整理
- 使用 EhCache 关于临时目录的一个注意事项
- Effective C++ 条款27
- 大根堆-小根堆-堆排序-C实现
- frame,iframe,frameset之间的关系与区别
- 黑马程序员----多态和内部类、异常
- 设置环境变量的三种方法
- 【MySQL】常见的mysql 进程state
- android开发中的自定义属性用法详解
- php 异步上传原理(iframe)
- codeforces #310 B B. Case of Fugitive(贪心)
- 共同父域下的单点登录
- 解决warning: Ignoring InnerClasses attribute for an anonymous inner class
- jsoncpp的使用