Java编程—在测试中考虑多态
面向对象编程有三大特性:封装、继承、多态。
封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。
继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。,同时继承也为实现多态做了铺垫。那么什么是多态呢?多态的实现机制又是什么?请看我一一为你揭开:
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。练习(1):创建一个Cycle类,它具有子类Unicycle,Bycycle,Tricycle.演示每一个类型的实例都可以经由ride()方法向上转型为Cycle.
向上转型就是允许将多种从同一基类的导出类看成同一类型。
多态方法调用就是允许一种类型表现出与其他相似类型之间的区别,只要他们是从同一基类导出而来的。这种区别由各个导出类型方法的具体不同实现而表现出来的,虽然这些方法都是由基类调用的。
publicclassTest1{ publicstaticvoidmain(String[]args){ Unicycleunicycle=newUnicycle("Unicycle"); Bicyclebicycle=newBicycle("Bicycle"); Tricycletricycle=newTricycle("Tricycle"); Cycle.ride(unicycle); Cycle.ride(bicycle); Cycle.ride(tricycle); } } classCycle{ privateStringname; publicCycle(Stringstr){ name=str; } publicstaticvoidride(Cyclec){ System.out.println(c.name+"isriding"); } } classUnicycleextendsCycle{ privateStringname; publicUnicycle(Stringstr){ super(str); name=str; } } classBicycleextendsCycle{ privateStringname; publicBicycle(Stringstr){ super(str); name=str; } } classTricycleextendsCycle{ privateStringname; publicTricycle(Stringstr){ super(str); name=str; } }
输出:
Unicycleisriding
Bicycleisriding
Tricycleisriding
在以上示例中,三种子类能被视作Cycle传入到方法ride()中就是向上转型。
但向上转型只是看成,而不是强制转换,所以最后方法调用的结果不同,这就是多态。
多态又称之为动态绑定。什么是动态绑定?
与动态绑定相反的是静态绑定。c语言所有方法都是默认静态绑定。静态绑定也称为前期绑定,就是在程序运行前就绑定完成。也就是说,代码写了什么样,就是什么样。
而动态绑定是直到运行时才去决定该方法调用该绑定哪个实体。
java中的所有static和final方法都是静态绑定,其他的所有方法都是动态绑定。
练习(2):在几何图形示例中添加@Override注解。
练习(3):在基类Shape.java中添加一个新方法,用于打印一条消息,但导出类中不要覆盖这个方法。请解释发生了什么。现在,在其中一个导出类中覆盖该方法,而在其他的导出类中不予覆盖,观察又有什么发生。最后,在所有的导出类中覆盖这个方法。
练习(4):向Shapes.java中添加一个新的Shape类型,并在main()方法中验证:多态对新类型的作用是否与在旧类型中的一样。
以上三个练习在一份示例中完成。
publicclassTest234{ privatestaticRandonShapeGeneratorgen=newRandonShapeGenerator(); publicstaticvoidmain(String[]args){ Shape[]shapes=newShape[9]; for(inti=0;i练习(5):以练习1为基础,才Cycle中添加wheels()方法,它将返回轮子的数量。修改ride()方法,让它调用wheels()方法,并验证多态起作用了。
在练习(1)的代码中给基类添加
publicvoidwheels(){ System.out.println("轮子数量是"+num); }然后在main中:
unicycle.wheels(); bicycle.wheels(); tricycle.wheels();最后输出结果都顺利输出了方法中的语句,证明多态确实起作用了。
练习(6):修改Music3.java,是what()方法成为根Object的toString()方法。试用System.out.pringtln()方法打印Instrument对象(不用向上转型).
练习(7):向Music3.java添加一个新的类型Instrument,并验证多态性是否作用于所添加的新类型.
练习(8):修改Music3.java,使其可以向Shapes.java中的方式那样随机创建Instrument对象。
三个练习将在一份代码完成。
publicclassTest678{ publicstaticvoidmain(String[]args){ Instrument[]orchestar={ newWind(), newPercussion(), newStringed(), newBrass(), newWoodwing() }; tuneAll(orchestar); newInstrumentni=newnewInstrument(); ni.play(Note.B_FLAT);//验证多态性是否适用于所添加的新类型,答案是确实适用。 } publicstaticvoidtune(Instrumentinstrument){ instrument.play(Note.MIDDLE_C);//无论传进声明子类,都播放MIDDLE_C } publicstaticvoidtuneAll(Instrument[]e){ for(Instrumenti: e){ tune(i); System.out.println(i.toString()); } } } classRandomInsFactory{//工厂类,用于随机生成一个Instrument的子类 privateRandomran=newRandom(47); publicInstrumentnext(){ switch(ran.nextInt(5)){ default: case0:returnnewWind(); case1:returnnewPercussion(); case2:returnnewStringed(); case3:returnnewBrass(); case4:returnnewWoodwing(); case5:returnnewnewInstrument(); } } } enumNote{//枚举类,存放了哪些音乐 MIDDLE_C,C_HARPE,B_FLAT; } classInstrument{ voidplay(Notenote){ System.out.println("Instrument.play():"+note); } Stringwhat(){ return"Instrument"; } voidadjust(){ System.out.println("adjustingInstrument"); } @Override publicStringtoString(){ ///添加一个toString方法,调用当前what方法, //子类会自动继承该方法并分别返回给自what()里的内容 returnwhat(); } } classWindextendsInstrument{ @Override voidplay(Notenote){ System.out.println("Wind.play():"+note); } @Override Stringwhat(){ return"Wind"; } @Override voidadjust(){ System.out.println("adjustingWind"); } } classPercussionextendsInstrument{ @Override voidplay(Notenote){ System.out.println("Percussion.play():"+note); } @Override Stringwhat(){ return"Percussion"; } @Override voidadjust(){ System.out.println("adjustingPercussion"); } } classStringedextendsInstrument{ @Override voidplay(Notenote){ System.out.println("Stringed.play():"+note); } @Override Stringwhat(){ return"Stringed"; } @Override voidadjust(){ System.out.println("adjustingStringed"); } } classBrassextendsWind{//继承自Wind @Override voidplay(Notenote){ System.out.println("Brass.play():"+note); } @Override voidadjust(){ System.out.println("adjustingBrass"); } } classWoodwingextendsWind{ @Override voidplay(Notenote){ System.out.println("Woodwing.play():"+note); } @Override Stringwhat(){ return"Woodwing"; } } classnewInstrumentextendsInstrument{//新添加的类型 @Override voidplay(Notenote){ System.out.println("newIns.play()"+note); } }练习(9):创建Rodent(啮齿动物):Mouse(老鼠),Gerbil(鼹鼠),Hamster(大颊鼠),等等这样一个的继承层次结构。在基类中,提供对所有的Rodent都通用的方法,在导出类中,根据特定的Rodent类型覆盖这些方法,以便观察它们执行不同的行为。创建一个Rodent数组,填充不同的Rodent类型,然后调用基类方法,观察发生了什么情况。
这跟前面Instrument的例子相似,在Instrument中有what()这个对所有Instrument都通用的方法,而在每个子类中我们都覆盖了这个方法并赋予了不同的行为,最终在main中创建了Instrument数组,调用了基类方法,最后得到的结果是不同类调用基类方法得到的输出是该类重写后的结果。不再重复。
练习(10):创建一个包含两个方法的基类。在第一个方法中可以调用第二个方法。然后产生一个继承自该基类的导出类,且覆盖基类中的第二个方法。为该导出类创建一个对象,将它向上转型为基类并调用第一个方法,解释发生的情况。
publicclassTest10{ publicstaticvoidmain(String[]args){ jileij=newdaochulei();//创建导出类的对象并转型为基类 j.first();//调用第一个方法 //结果输出daochuleiisrunning //原因,就像前面提过的,导出类继承了基类的所有东西,没有重写的方法隐藏了起来 //其实在daochulei中还隐士的有voidfirst()这个方法里调用了自身重写的second() //当daochulei调用first()方法后,它就调用了自身重写的second()方法。 //导出类调用基类方法其实不是真的调用,而是调用自身继承自基类的方法, //只不过这个方法没重写时,内部形式与基类相同 } } classjilei{ voidfirst(){//调用第二个方法 second(); } voidsecond(){ System.out.println("firstisrunning"); } } classdaochuleiextendsjilei{ @Override voidsecond(){ System.out.println("daochuleiisrunning"); } }练习(11)跳过
练习(12):修改练习(9),使其能够演示基类和导出类的初始化顺序。然后向基类和导出类中添加成员对象,并说明构建期间初始化发生的顺序。
publicclassTest912{ publicstaticvoidmain(String[]args){ newHamster(); } } classRodent{ publicRodent(){ shanghai=100; System.out.println("Rodent"); } privateintshanghai; publicvoidbite(){ System.out.println("造成伤害"+shanghai+"点"); } } classMouseextendsRodent{ privateintsh; publicMouse(){ sh=1000; System.out.println("Mouse"); } @Override publicvoidbite(){ System.out.println("造成伤害"+sh+"点"); } } classGerbilextendsMouse{ privateintshang; publicGerbil(){ shang=2000; System.out.println("Gerbil"); } @Override publicvoidbite(){ System.out.println("造成伤害"+shang+"点"); } } classHamsterextendsGerbil{ privateMousemouse=newMouse();//成员对象 //该类初始化输出结果 //Rodent //Mouse //Gerbil //Rodent //Mouse //Hamster //可以分析出,初始化时先调用基类的构造方法, //然后初始化成员变量,因为其中有Mouse这个成员对象,所有对Mouse进行初始化, //完成后再调用自身的构造方法 privateinthai; publicHamster(){ hai=3000; System.out.println("Hamster"); } @Override publicvoidbite(){ System.out.println("造成伤害"+hai+"点"); } }总结
以上就是本文关于Java编程—在测试中考虑多态的全部内容,希望对大家有所帮助。欢迎参阅:Java面向对象编程(封装/继承/多态)实例解析、Java实现微信公众平台朋友圈分享功能详细代码、Java编程BigDecimal用法实例分享等,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!