Java Review (十一、面向对象----多态)

简介: Java Review (十一、面向对象----多态)

       

文章目录

多态是同一个行为具有多个不同表现形式或形态的能力。

多态性

Java 引用变量有两个类型 :一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定,如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)

多态实例

class BaseClass {
  public int book = 6;
  public void base() {
    System.out.println("父类的普通方法");
  }
  public void test() {
    System.out.println("父类的被覆盖的方法");
  }
}
public class SubClass extends BaseClass {
  // 重新定义一个 book 实例变量隐藏父类 book 实例变量
  public String book = "子类书籍 ";
  public void test() {
    System.out.println(" 子类的覆盖父类的方法");
  }
  public void sub() {
    System.out.println("子类的普通方法 ");
  }
  public static void main(String[] args) {
    // 下面编译时类型和运行时类型完全一样 因此不存在多态
    BaseClass bc = new BaseClass();
    // 输出
    System.out.println(bc.book);
    // 下面两次调用将执行 BaseClass的方法
    bc.base();
    bc.test();
    // 下面编译时类型和运行时类型完全 样,因此不存在多态
    SubClass sc = new SubClass();
    // 输出 轻量级 Java EE 企业应用实战"
    System.out.println(sc.book);
    // 下面调用将执行从父类继承到的 base ()方法
    sc.base();
    // 下面调用将执行当前类的 test ()方法
    sc.test();
    // 下面编译时类型和运行时类型不 样,多态发生
    BaseClass ploymophicBc = new SubClass();
    // 输出表明访问的是父类对象的实例变量
    System.out.println(ploymophicBc.book);
    // 下面调用将执行从父类继承到的 base ()方法
    ploymophicBc.base();
    // 下面调用将执行当前类的 test ()方法
    ploymophicBc.test();
    // 因为 ploymophicBC 的编译时类型是 BaseClass
    // BaseClass 类没有提供 sub ()方法,所以下面代码编译时会出现错误
    // plonophicBc.sub()
  }
}

结果:

6
父类的普通方法
父类的被覆盖的方法
子类书籍 
父类的普通方法
 子类的覆盖父类的方法
6
父类的普通方法
 子类的覆盖父类的方法
  • 父类引用指向子类对象时,多态发生了

引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法 。因此,编写 Java  代码时,引用变量只能调用声明该变量时所用类里包含的方法。 例如,通过 Object p = new Person()代码定义一个变量  ,则这个变量只能调用Object 类的方法,而不能调用 Person 类里定义的方法

引用变量的强制类型转换

编写Java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如果需要让这个引用变量调用它运行时类型的方法,则必须把 它强制类型转换成运行时类型,强制类型转换需要借助于类型转换运算符。

类型转换运算符是小括号,类型转换运算符的用法是:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。类型转换运算符可以将一个基本类型变量转换成另一个类型。

除此之外,这个类型转换运算符还可以将一个引用类型变量转换成其子类类型。这种强制类型转换不是万能的,当进行强制类型转换时需要注意:

  • 基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括整数型、字符型和浮 点型。但数值类型和布尔类型之间不能进行类型转换。
  • 引用类型之间的转换只能在具有继承关系的两个类型之间进行,如果是两个没有任何继承关系  的类型,则无法进行类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子  类类型,则这个对象必须实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将在运行时引发ClassCastException异常。

下面是进行强制类型转换的示范程序。下面程序详细说明了哪些情况可以进行类型转换,哪些情况不可以进行类型转换。

引用类型强制转换实例

public class ConversionTest {
  public static void main(String[] args) {
    double d = 13.4;
    long l = (long) d;
    System.out.println(l);
    int in = 5;
    //试图把 个数值类型的变量转换为 boolean 类型,下面代码编译出错
    //编译时会提示:不可转换的类型
   // boolean b = (boolean)in ; 
    Object obj = "Hello";
    // obj 变量的编译时类型为 Object,Object、String 存在继承关系,可以强制类型转换
    //而且 obj 变量的实际类型是 String ,所以运行时也可通过
    String objStr = (String) obj;
    System.out.println(objStr);
   // 定义一个 objpri 变量,编译时类型为 bject ,实际类型为 Integer
    Object objpri = Integer.valueOf(5);
    // objpri 变量的编译时类型为 Object, objPr的运行时类型为 Integer
   // Object Integer 存在继承关系
   // 可以强制类型转换,而 objpri 变量的实际类型是 Integer
   // 所以下面代码运行时引发 ClassCastException 异常
    //String str = (String)objPri;
  }
}

当把子类对象赋给父类引用变量时,被称为向上转型( upcasting  ),这种转型总是可以成功的,这也从另一个侧面证实了子类是一种特殊的父类  。这种转型只是表明这个引用变量的编译时类型是父类,但实际执行它的方法时,依然表现出子类对象的行为方式。但把一个父类对象赋给子类引用交量时,就需要进行强制类型转换,而且还可能在运行时产ClassCastException  异常,使用 instanceof 运算符可以让强制类型转换更安全。

instanceof 运算符

instanceof运算符的前一个操作数通常是一个引用类型变量,后一个操作数通常是一个类(也可以是接口),它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回true,否则返回false。

在使用instanceof运算符时需要注意:instanceof运算符前面操作数的编译时类型要么与后面的类相 同,要么与后面的类具有父子继承关系,否则会引起编译错误。

下面程序示范了 instanceof运算符的用法。

instanceof运算符实例

public class InstanceTest {
  public static void main(String[] args) {
    // 声 hello 时使用 Object 类,则 hello 的编译类型是 Object
    // Object 是所有类的父类,但 hello 量的实际类型是 String
    Object hello = "Hello";
    // String Object 类存在继承关系,可以进行 instanceof 运算 返回 true
    System.out.println(" 字符串是否是 Object 类的实例" + (hello instanceof Object));
    System.out.println("字符串是否是 String 类的实例:" + (hello instanceof String)); // 返回 true
    // Math Object 类存在继承关系,可以进行 instanceof 运算。返回 false
    System.out.println("字符串是否是 Math 类的实例" + (hello instanceof Math));
    // Stri 呵实现了 Comparable 接口,所以返回 true
    System.out.println("字符串是否是 Comparable 接口的实例" + (hello instanceof Comparable));
    String a = "He llo";
    // String 类与 Math 类没有继承关系,所以下面代码编译无法通过
    // System.out.println( "字符串是否是 Math 类的实例" + (a instanceof Math));
  }
}

多态实现方式

  • 重写
  • 接口
  • 抽象类和抽象方法


参考:

【1】:《疯狂Java讲义》

【2】:https://www.runoob.com/java/java-polymorphism.html

【3】:《Java核心技术 卷一》

目录
相关文章
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
22 2
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第10天】Java零基础教学篇,手把手实践教学!
24 4
|
2月前
|
Java 编译器 程序员
Java多态背后的秘密:动态绑定如何工作?
本文介绍了Java中多态的实现原理,通过动态绑定和虚拟方法表,使得父类引用可以调用子类的方法,增强了代码的灵活性和可维护性。文中通过具体示例详细解析了多态的工作机制。
45 4
|
3月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
2月前
|
Java
java继承和多态详解
java继承和多态详解
43 5
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第1天】Java零基础教学篇,手把手实践教学!
29 1
|
2月前
|
安全 Java 编译器
【一步一步了解Java系列】:重磅多态
【一步一步了解Java系列】:重磅多态
20 3
|
3月前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
35 4
|
3月前
|
Java
接口和抽象类【Java面向对象知识回顾②】
本文讨论了Java中抽象类和接口的概念与区别。抽象类是不能被实例化的类,可以包含抽象和非抽象方法,常用作其他类的基类。接口是一种纯抽象类型,只包含抽象方法和常量,不能被实例化,且实现接口的类必须实现接口中定义的所有方法。文章还比较了抽象类和接口在实现方式、方法类型、成员变量、构造方法和访问修饰符等方面的不同,并探讨了它们的使用场景。
接口和抽象类【Java面向对象知识回顾②】
|
3月前
|
Java
Java 多态趣解
在一个阳光明媚的午后,森林中的动物们举办了一场别开生面的音乐会。它们组成了一支乐队,每种动物都有独特的演奏方式。通过多态的魅力,狗、猫和青蛙分别展示了“汪汪”、“喵喵”和“呱呱”的叫声,赢得了观众的阵阵掌声。熊指挥着整个演出,每次调用 `perform()` 方法都能根据不同的动物对象唤起对应的 `makeSound()` 方法,展现了 Java 多态性的强大功能,让整场音乐会既有趣又充满表现力。