Java编程—在测试中考虑多态

面向对象编程有三大特性:封装、继承、多态。

封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。

继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。,同时继承也为实现多态做了铺垫。那么什么是多态呢?多态的实现机制又是什么?请看我一一为你揭开:

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。练习(1):创建一个Cycle类,它具有子类Unicycle,Bycycle,Tricycle.演示每一个类型的实例都可以经由ride()方法向上转型为Cycle.

向上转型就是允许将多种从同一基类的导出类看成同一类型。

多态方法调用就是允许一种类型表现出与其他相似类型之间的区别,只要他们是从同一基类导出而来的。这种区别由各个导出类型方法的具体不同实现而表现出来的,虽然这些方法都是由基类调用的。

public class Test1 {
  public static void main(String[] args){
    Unicycle unicycle = new Unicycle("Unicycle");
    Bicycle bicycle = new Bicycle("Bicycle");
    Tricycle tricycle = new Tricycle("Tricycle");
    Cycle.ride(unicycle);
    Cycle.ride(bicycle);
    Cycle.ride(tricycle);
  }
}
class Cycle{
  private String name;
  public Cycle(String str){
    name = str;
  }
  public static void ride(Cycle c){
    System.out.println(c.name + "is riding");
  }
}
class Unicycle extends Cycle{
  private String name;
  public Unicycle(String str) {
    super(str);
    name = str;
  }
}
class Bicycle extends Cycle{
  private String name;
  public Bicycle(String str) {
    super(str);
    name = str;
  }
}
class Tricycle extends Cycle {
  private String name;
  public Tricycle(String str) {
    super(str);
    name = str;
  }
}

输出:

Unicycleis riding

Bicycleis riding

Tricycleis riding

在以上示例中,三种子类能被视作Cycle传入到方法ride()中就是向上转型。

但向上转型只是看成,而不是强制转换,所以最后方法调用的结果不同,这就是多态。

多态又称之为动态绑定。什么是动态绑定?

与动态绑定相反的是静态绑定。c语言所有方法都是默认静态绑定。静态绑定也称为前期绑定,就是在程序运行前就绑定完成。也就是说,代码写了什么样,就是什么样。

而动态绑定是直到运行时才去决定该方法调用该绑定哪个实体。

java中的所有static和final方法都是静态绑定,其他的所有方法都是动态绑定。

练习(2):在几何图形示例中添加@Override注解。

练习(3):在基类Shape.java中添加一个新方法,用于打印一条消息,但导出类中不要覆盖这个方法。请解释发生了什么。现在,在其中一个导出类中覆盖该方法,而在其他的导出类中不予覆盖,观察又有什么发生。最后,在所有的导出类中覆盖这个方法。

练习(4):向Shapes.java中添加一个新的Shape类型,并在main()方法中验证:多态对新类型的作用是否与在旧类型中的一样。

以上三个练习在一份示例中完成。

public class Test234 {
  private static RandonShapeGenerator gen = new RandonShapeGenerator();
  public static void main(String[] args){
    Shape[] shapes = new Shape[9];
    for (int i = 0; i < shapes.length; i++) {
      shapes[i] = gen.next();
    }
    for (Shape s :
        shapes) {
      s.draw();
      s.newMethod();//每个子类都调用了一次添加的新方法,
      // 因为子类继承父类自然把所有方法都继承过去了,只不过没有显示出来,
      // 其实是隐式的存在的,子类调用的其实是继承自父类的没有重写的方法,
      // 看起来像是调用了父类的方法
    }
    Shape s = new Recf();//这里是声明了一个Shape类型的引用,但实际的对象还是Recf.
    s.draw();//输出的是Recf类重写的方法,证明多态对新类的作用于在旧类中是一样的
  }
}
class Shape{//基类
  public void draw(){}
  public void erase(){}
  public void newMethod(){
    System.out.println("new method");//添加的新方法
  }
}
class Circle extends Shape{
  @Override//添加注解,一般IDE可以自动添加
  public void draw() {
    System.out.println("draw circle");
  }
  @Override
  public void erase() {
    System.out.println("erase circle");
  }
}
class Square extends Shape{
  @Override
  public void draw() {
    System.out.println("draw Square");
  }
  @Override
  public void erase() {
    System.out.println("erase Square");
  }
  @Override
  public void newMethod() {
    System.out.println("Square new method");
    //重写后该类输出内容就发生改变,没有重写时该类的该方法与父类运行结果相同
    //无论重写与否,其实调用的都是自身的方法
    //只是没有重写时方法调用结果与父类的相同
  }
}
class Triangle extends Shape{
  @Override
  public void draw() {
    System.out.println("draw Triangle");
  }
  @Override
  public void erase() {
    System.out.println("erase Triangle");
  }
}
class Recf extends Shape{//新添加的方法
  @Override
  public void draw() {
    System.out.println("recf draw");
  }
  @Override
  public void erase() {
    System.out.println("recf erase");
  }
}
class RandonShapeGenerator{//是一种工厂,用以随机获取一种Shape的子类
  private Random rand = new Random(100);
  public Shape next(){
    switch (rand.nextInt(3)){
      default:
      case 0:return new Circle();
      case 1:return new Square();
      case 2:return new Triangle();
    }
  }
}

练习(5):以练习1为基础,才Cycle中添加wheels()方法,它将返回轮子的数量。修改ride()方法,让它调用wheels()方法,并验证多态起作用了。

在练习(1)的代码中给基类添加

public void wheels(){
    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对象。

三个练习将在一份代码完成。

public class Test678 {
  public static void main(String[] args){
    Instrument[] orchestar = {
        new Wind(),
        new Percussion(),
        new Stringed(),
        new Brass(),
        new Woodwing()
    };
    tuneAll(orchestar);
    newInstrument ni = new newInstrument();
    ni.play(Note.B_FLAT);//验证多态性是否适用于所添加的新类型,答案是确实适用。
  }
  public static void tune(Instrument instrument){
    instrument.play(Note.MIDDLE_C);//无论传进声明子类,都播放MIDDLE_C
  }
  public static void tuneAll(Instrument[] e){
    for (Instrument i :
        e) {
      tune(i);
      System.out.println(i.toString());
    }
  }
}
class RandomInsFactory{//工厂类,用于随机生成一个Instrument的子类
  private Random ran = new Random(47);
  public Instrument next(){
    switch (ran.nextInt(5)){
      default:
      case 0:return new Wind();
      case 1:return new Percussion();
      case 2:return new Stringed();
      case 3:return new Brass();
      case 4:return new Woodwing();
      case 5:return new newInstrument();
    }
  }
}
enum Note{//枚举类,存放了哪些音乐
  MIDDLE_C,C_HARPE,B_FLAT;
}
class Instrument {
  void play(Note note){
    System.out.println("Instrument.play() : " + note);
  }
  String what(){
    return "Instrument";
  }
  void adjust(){
    System.out.println("adjusting Instrument");
  }
  @Override
  public String toString() {
    // /添加一个toString方法,调用当前what方法,
    // 子类会自动继承该方法并分别返回给自what()里的内容
    return what();
  }
}
class Wind extends Instrument{
  @Override
  void play(Note note) {
    System.out.println("Wind.play() : " + note);
  }
  @Override
  String what() {
    return "Wind";
  }
  @Override
  void adjust() {
    System.out.println("adjusting Wind");
  }
}
class Percussion extends Instrument{
  @Override
  void play(Note note) {
    System.out.println("Percussion.play() : " + note);
  }
  @Override
  String what() {
    return "Percussion";
  }
  @Override
  void adjust() {
    System.out.println("adjusting Percussion");
  }
}
class Stringed extends Instrument{
  @Override
  void play(Note note) {
    System.out.println("Stringed.play() : " + note);
  }
  @Override
  String what() {
    return "Stringed";
  }
  @Override
  void adjust() {
    System.out.println("adjusting Stringed");
  }
}
class Brass extends Wind{//继承自Wind
  @Override
  void play(Note note) {
    System.out.println("Brass.play() : " + note);
  }
  @Override
  void adjust() {
    System.out.println("adjusting Brass");
  }
}
class Woodwing extends Wind{
  @Override
  void play(Note note) {
    System.out.println("Woodwing.play() : " + note);
  }
  @Override
  String what() {
    return "Woodwing";
  }
}
class newInstrument extends Instrument{//新添加的类型
  @Override
  void play(Note note) {
    System.out.println("newIns.play()" + note);
  }
}

练习(9):创建Rodent(啮齿动物):Mouse(老鼠),Gerbil(鼹鼠),Hamster(大颊鼠),等等这样一个的继承层次结构。在基类中,提供对所有的Rodent都通用的方法,在导出类中,根据特定的Rodent类型覆盖这些方法,以便观察它们执行不同的行为。创建一个Rodent数组,填充不同的Rodent类型,然后调用基类方法,观察发生了什么情况。

这跟前面Instrument的例子相似,在Instrument中有what()这个对所有Instrument都通用的方法,而在每个子类中我们都覆盖了这个方法并赋予了不同的行为,最终在main中创建了Instrument数组,调用了基类方法,最后得到的结果是不同类调用基类方法得到的输出是该类重写后的结果。不再重复。

练习(10):创建一个包含两个方法的基类。在第一个方法中可以调用第二个方法。然后产生一个继承自该基类的导出类,且覆盖基类中的第二个方法。为该导出类创建一个对象,将它向上转型为基类并调用第一个方法,解释发生的情况。

public class Test10 {
  public static void main(String[] args){
    jilei j = new daochulei();//创建导出类的对象并转型为基类
    j.first();//调用第一个方法
    //结果输出daochulei is running
    //原因,就像前面提过的,导出类继承了基类的所有东西,没有重写的方法隐藏了起来
    //其实在daochulei中还隐士的有void first()这个方法里调用了自身重写的second()
    //当daochulei调用first()方法后,它就调用了自身重写的second()方法。
    //导出类调用基类方法其实不是真的调用,而是调用自身继承自基类的方法,
    // 只不过这个方法没重写时,内部形式与基类相同
  }
}
class jilei{
  void first(){//调用第二个方法
    second();
  }
  void second(){
    System.out.println("first is running");
  }
}
class daochulei extends jilei{
  @Override
  void second() {
    System.out.println("daochulei is running");
  }
}

练习(11)跳过

练习(12):修改练习(9),使其能够演示基类和导出类的初始化顺序。然后向基类和导出类中添加成员对象,并说明构建期间初始化发生的顺序。

public class Test912 {
  public static void main(String[] args){
    new Hamster();
  }
}
class Rodent{
  public Rodent(){
    shanghai = 100;
    System.out.println("Rodent");
  }
  private int shanghai;
  public void bite(){
    System.out.println("造成伤害" +shanghai + "点" );
  }
}
class Mouse extends Rodent{
  private int sh;
  public Mouse(){
    sh = 1000;
    System.out.println("Mouse");
  }
  @Override
  public void bite() {
    System.out.println("造成伤害" +sh + "点" );
  }
}
class Gerbil extends Mouse{
  private int shang;
  public Gerbil(){
    shang = 2000;
    System.out.println("Gerbil");
  }
  @Override
  public void bite() {
    System.out.println("造成伤害" +shang + "点" );

  }
}
class Hamster extends Gerbil{
  private Mouse mouse = new Mouse();//成员对象
  //该类初始化输出结果
  //Rodent
//  Mouse
//  Gerbil
//  Rodent
//  Mouse
//  Hamster
  //可以分析出,初始化时先调用基类的构造方法,
  // 然后初始化成员变量,因为其中有Mouse这个成员对象,所有对Mouse进行初始化,
  // 完成后再调用自身的构造方法
  private int hai;
  public Hamster(){
    hai = 3000;
    System.out.println("Hamster");
  }
  @Override
  public void bite() {
    System.out.println("造成伤害" + hai + "点" );

  }
}

总结

以上就是本文关于Java编程—在测试中考虑多态的全部内容,希望对大家有所帮助。欢迎参阅:Java面向对象编程(封装/继承/多态)实例解析、Java实现微信公众平台朋友圈分享功能详细代码、Java编程BigDecimal用法实例分享等,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • java 中继承和多态详细介绍

    继承和多态 一.this super关键字 1.this: 可以在构造器中的第一代码中调用本类中的其他构造器.this(参数) 非类方法参数中隐式传入的参数,表示调用当前方法的对象. 2.super: 可以在构造器的第一句代码调用父类的构造器.super(参数). 非静态方法中表示继承的父类对象,可以调用父类方法和属性. 二.方法的覆写: 子类重新实现了和父类一样的方法.访问修饰和异常都必须至少和父类的相同或者更大的范围. 三.方法的重载: 相同的方法的名字不同的参数列表. 四.多态: java

  • Java多态和实现接口的类的对象赋值给接口引用的方法(推荐)

    接口的灵活性就在于"规定一个类必须做什么,而不管你如何做". 我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似. //定义接口InterA interface InterA { void fun(); } //实现接口InterA的类B class B implements InterA { public void fun() { System.out.pri

  • java中多态概念、实现原理详解

    一.什么是多态? 1.多态的定义 指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用) 2.多态的作用 消除类型之间的耦合关系 3.多态的说明 近代网络小说泛滥,我们可以用它来举一个例子 某日你看见你手机上有多部小说同时更新了,比如有大主宰,雪鹰领主,龙王传说-在这里我们可以描述成如下: 小说a=大主宰 小说b=雪鹰领主 小说c=龙王传说 - 这里所表现的就是多态,大主宰,雪鹰领主,龙王传说都是小说的子类,我们仅仅可以通过小说这个

  • java 多态性详解及简单实例

    Java中多态性的实现 什么是多态 面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同的行为方式.(发送消息就是函数调用) 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法. 多态的作用:消除类型之间的耦合关系. 现实中,关于多态的例

  • java数组、泛型、集合在多态中的使用及对比

    我们在使用数组,泛型集合的过程中不可避免的会碰到多态,或者说什么情况下能如何使用父数组引用子数组(集合.泛型)呢? 数组在多态中的使用 元素为父类型的数组引用可指向元素为子类型的数组对象 当数组被调用时由当前引用确定数组元素的类型 当元素为父类型的数组引用指向了元素为子类型的数组对象时,不可通过父类型的数组引用向集合加入除子类型及其下层级的之外的元素(可通过编译,运行过程出错) /*验证: *animals和dogs可以引用同一个数组 *当animals引用数组时,数组元素为Animal类型 *

  • Java多态(动力节点Java学院整理)

    什么是多态 1. 面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 2. 多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同的行为方式.(发送消息就是函数调用) 3. 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法. 4. 多态的作用:消除类型之间的耦合关系. 5. 现实中,关于多

  • Java编程—在测试中考虑多态

    面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承是为了重用父类代码.两个类若存在IS-A的关系就可以使用继承.,同时继承也为实现多态做了铺垫.那么什么是多态呢?多态的实现机制又是什么?请看我一一为你揭开: 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底

  • Java编程删除链表中重复的节点问题解决思路及源码分享

    一. 题目 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 二. 例子 输入链表:1->2->3->3->4->4->5 处理后为:1->2->5 三. 思路 个人感觉这题关键是注意指针的指向,可以定义一个first对象(值为-1,主要用于返回操作后的链表),first.next指向head,定义一个last同样指向first(主要用于操作记录要删除节点的前一个节点),定义一个p指向head,指向当前节点.

  • java编程调用存储过程中得到新增记录id号的实现方法

    本文实例讲述了java编程调用存储过程中得到新增记录id号的实现方法.分享给大家供大家参考,具体如下: 关于ms sql server2000的存储过程,主要作用是在表test中插入一条记录,然后得到新增加记录的id号. test表三个字段: ID:自动增长 yhm:用户名 字符串类型 kl: 密码   字符串类型 那么在java程序中如何调用这个存储过程才能实现,得到新增加记录的id号 存储过程如下: CREATE PROCEDURE yh_insert @yhm varchar(50),@k

  • java 对象实例化过程中的多态特性解析

    目录 java 对象实例化过程中的多态特性 通过案例说明 通过上述代码 java对象的三个特性(封装.继承.多态) 1.封装 2.继承 3.多态 java 对象实例化过程中的多态特性 执行对象实例化过程中遵循多态特性 ==> 调用的方法都是实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法. 通过案例说明 package com.njau.test1; class Test { public static void main(S

  • Java编程实现游戏中的简单碰撞检测功能示例

    本文实例讲述了Java编程中的简单碰撞检测功能.分享给大家供大家参考,具体如下: 今天在家正在写一个坦克大战的小游戏来玩,遇到了一个简单的圆和圆的碰撞检测的小问题, 碰撞检测的过程处理主要有以下三步: 1.碰撞检测(Collision Detection):返回两个或多个物体是否发生碰撞的布尔判断. 2.碰撞确定(Collision Determination):找到物体之间实际相交位置. 3.碰撞响应(Collision Response):针对两个物体之间的碰撞决定采取何种操作. 下面是关于

  • Java编程在方法中哪些时候需要参数

    目录 Java在方法中什么时候需要参数 Java中类的无参,带参方法重点总结 类的方法定义类的某种行为(或功能) 方法的返回值(两种情况) 成员变量和局部变量 成员变量和局部变量的区别 定义带参数的方法 调用带参数的方法 小结 Java在方法中什么时候需要参数 可能有很多人在学习Java的过程中,在构建方法时总是不知道何时该写参数,何时不写,那么接下来我们写一个最简单的例子来说明有参数的重要性. 如图中的例子,如果我们要计算b和c的值,只需要在调用add()方法的时候把b和c替换成你想计算的数就

  • Java编程实现递增排序链表的合并

    题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 解答: /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if(list1==null)

  • Java编程GUI中的事件绑定代码示例

    程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定 静态绑定: 在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现.例如:C. 针对java简单的可以理解为程序编译期的绑定:这里特别说明一点,java当中的方法只有final,static,private和构造方法是前期绑定 动态绑定 后期绑定:在运行时根据具体对象的类型进行绑定. 若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间

  • 整理Java编程中字符串的常用操作方法

    字符 一般情况下,当我们处理字符时,我们用原始数据类型 char. 示例 char ch = 'a'; // Unicode for uppercase Greek omega character char uniChar = '\u039A'; // an array of chars char[] charArray ={ 'a', 'b', 'c', 'd', 'e' }; 然而在开发中,我们会遇到需要使用对象而不是原始数据类型的情况.为了达到这个需求.Java 为原始数据类型 char

  • 深入理解Java编程中异常处理的优劣

    Java编程中的异常处理是一个很常见的话题了,几乎任何一门介绍性的Java课程都会提到异常处理.不过,我认为很多人其实没有真正掌握正确处理异常情况的方法和策略,最多也就不过了解个大概,知道概念.我想对三种不同程度和质量的Java异常处理进行了讨论,所阐述的处理异常的方式按手法的高下分为:好,不好和恶劣三种.同时提供了一些解决这些问题的技巧.首先解释一些java异常处理中必须搞清楚的定义和机制.Java语言规范将自Error类或RuntimeException类衍生出来的任何违例都称作"不可检查&

随机推荐