JAVA多态——向上转型

之前在写多态的博客时,向上转型方面的知识点没有总结。

首先再总结一下多态。

多态

多态是面向对象编程中的一个重要概念,允许不同类的对象对同一消息做出不同的响应。多态性主要通过继承和接口实现,使得代码更加灵活和可扩展。

多态的实现方式

方法重载(Overloading)

方法重载是指在同一个类中定义多个同名方法,但这些方法的参数列表不同(参数类型、数量或顺序不同)。编译器根据调用时传递的参数来决定调用哪个方法。

java 复制代码
class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

方法重写(Overriding)

方法重写是指子类重新定义父类中已有的方法。子类对象调用该方法时,会执行子类中的实现,而不是父类中的实现。

java 复制代码
class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("Dog barks");
    }
}

接口实现

接口定义了一组方法签名,不同的类可以实现同一个接口,并提供不同的实现。通过接口引用调用方法时,实际执行的是具体实现类中的方法。

java 复制代码
interface Shape {
    void draw();
}

class Circle implements Shape {
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Square implements Shape {
    public void draw() {
        System.out.println("Drawing a square");
    }
}

多态的优势

代码复用

通过继承和接口,可以减少代码重复,提高代码的可维护性。

扩展性

新增类时,无需修改现有代码,只需实现相应的接口或继承父类即可。

灵活性

多态允许程序在运行时根据对象的实际类型调用相应的方法,增强了程序的灵活性。

多态的应用场景

框架设计

在框架设计中,多态性允许开发者通过实现特定接口或继承特定类来扩展框架功能。

插件系统

插件系统通常利用多态性,允许开发者通过实现特定接口来添加新功能。

事件处理

在事件驱动编程中,多态性允许不同的事件处理器对同一事件做出不同的响应。

多态性是面向对象编程的核心特性之一,合理使用多态可以显著提高代码的质量和可维护性。

向上转型(Upcasting)是面向对象编程中的一个概念,指的是将子类对象赋值给父类引用。这种转型是自动进行的,不需要显式地进行类型转换。向上转型的主要目的是实现多态性,允许父类引用调用子类对象的方法。

向上转型:

定义

将子类对象赋值给父类引用的过程,称为向上转型。

  • 语法父类类型 引用变量 = new 子类类型();
  • 本质:子类对象被视为父类类型,是一种隐式转换,无需显式声明。

向上转型的特点

向上转型是安全的,因为子类对象包含了父类的所有属性和方法。通过父类引用,可以访问子类对象中继承自父类的方法和属性,但不能直接访问子类特有的方法和属性。

向上转型的示例

以下是一个简单的Java示例,展示了向上转型的使用:

java 复制代码
package com.qcby;
 
public class A {
    public String name;
    public int age;
    public void run(){
        System.out.println("A跑的很快");
    }
    public void eat(String name){
        System.out.println(name+ "吃的很多");
    }
}
--------------------------------------------
package com.qcby;
 
public class B extends A{
    public String sex;
    public double height;
    public void fly(){
        System.out.println("B飞得很高");
    }
    public void run(){
        System.out.println("B跑的很快");
    }
}
 
-----------------------------------------
package com.qcby;
 
public class Test {
    public static void main(String[] args) {
        A ab = new B();
        ab.name = "张三";
        ab.age = 18;
        ab.run();
        ab.eat("张三");
    }
}
-------------------------------------------
//运行结果:
//B跑的很快
//张三吃的很多

在这个示例中,,A ab = new B(); 这行代码正是向上转型(Upcasting)的典型体现。

java 复制代码
public class Test {
    public static void main(String[] args) {
        A ab = new B();  // 向上转型
        ab.name = "张三";
        ab.age = 18;
        ab.run();      // 调用B类的run()
        ab.eat("张三");  // 调用A类的eat()
     
    }
}
  • 隐式转换new B() 是子类对象,自动转换为父类 A 类型。
  • 编译期检查 :编译器只允许调用父类 A 中定义的方法和属性。
  • 运行期行为 :重写的方法会根据实际对象类型(B)动态调用。

向上转型的特性

变量的类型是父类类型,因此只能访问父类中定义的方法和属性。子类特有的方法和属性在向上转型后无法访问。

只能访问父类定义的成员

java 复制代码
A ab = new B();
ab.name = "张三";  // A类中定义的属性
// ab.sex = "男";  // 编译错误:A类中没有sex属性

重写方法优先调用子类实现

java 复制代码
// B类重写了run()方法
ab.run();  // 输出:"B跑的很快"

无法直接调用子类特有的方法

java 复制代码
// B类的fly()方法是特有方法
// ab.fly();  // 编译错误

编译过程

编译阶段:

Test 类被编译成 Test.class。

A 类被编译成 A.class。

B 类被编译成 B.class,需要 A.class 的支持。
类加载阶段:

A.class 和 B.class 被加载到方法区。
运行阶段:

创建 B 类型的对象,并将其赋值给 A 类型的变量 ab。

调用 ab.run(),由于 ab 实际上引用的是 B 类对象,因此调用 B 中的 run 方法。

调用 ab.eat("张三"),由于 eat 方法在 A 类中定义且未在 B 类中重写,因此调用 A 类中的 eat 方法。

列题:

java 复制代码
public class A {
    public String show(D obj) {
        return ("A and D");
    }
    public String show(A obj) {
        return ("A and A");
    } 
}
 
public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}
 
public class C extends B{
}
 
public class D extends B{
}
 
public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));      
    }
}

输出结果:

A and A

解析:a1 调用的是 A 类中的方法,其中有 show(D) 和 show(A) 两个重载方法。由于 b 被向上转型到 A,a1.show(b) 调用的是 A 类中的 show(A) 方法,输出 A and A。这在 a1 是 A 类型,且 b 的类型向上转型的情况下是准确的。

A and A

解析:和第一个选项相似,a1 调用的是 A 类的方法,C继承于B,b 向上转型到 A,因此调用 show(A) 方法,输出 A and A。这个选项与第一个选项的解释相同。

A and D

解析:a1 调用的是 A 类的方法,其中有 show(D) 和 show(A) 两个重载方法。如果 b 实际上是 D 类型,那么在 A 类中调用 show(D) 方法会输出 A and D。

B and A

解析:a2 是 B 类型,但由于方法重写,它调用的是 B 类中的 show(A) 方法。这个选项需要 B 类重写 A 类中的 show(A) 方法并且输出 B and A。

B and A

解析:类似于第四个选项,a2 是 B 类型,调用的是 B 类中的 show(A) 方法。这里需要 a2 向上转型到 A,调用 B 类的 show(A) 方法,输出 B and A。

A and D

解析:与第三个选项相似,这个结果表明 a2 调用的是 A 类中的 show(D) 方法,输出 A and D。需要 b 实际上是 D 类型或其子类。

B and B

解析:b 是 B 类型,B 类中的方法可以被调用,可能包括 show(D) 和 show(A) 方法,这里输出 B and B。这表明 B 类中的方法重写了 A 类中的方法。

B and B

解析:和第七个选项类似,这个结果表明 b 是 B 类型,由于继承和方法重写,输出 B and B。

A and D

解析:b 是 B 类型,调用了 A 类中的 show(D) 方法,输出 A and D。这表明 b 实际上是 D 类型或其子类,且重载方法选择为 show(D)。

  • a1 是 A 类型的引用,指向 A 对象
  • 对于 a1.show (b) 和 a1.show (c),参数 b 和 c 都是 B 的子类,向上转型为 A,因此调用 show (A)
  • 对于 a1.show (d),参数 d 是 D 类型,直接匹配 show (D)
  • a2 是 A 类型的引用,指向 B 对象
  • 对于 a2.show (b) 和 a2.show (c),参数 b 和 c 向上转型为 A,调用重写的 show (A) 方法
  • 对于 a2.show (d),参数 d 是 D 类型,直接匹配 show (D) 方法(B 中未重写该方法,因此调用 A 的实现)
  • b 是 B 类型的引用,指向 B 对象
  • 对于 b.show (b),直接匹配 B 的 show (B) 方法
  • 对于 b.show (c),参数 c 是 B 的子类,向上转型为 B,调用 B 的 show (B) 方法
  • 对于 b.show (d),参数 d 是 D 类型,向上转型为 A,调用 B 的 show (A) 方法(这里可能存在争议,根据 Java 的方法匹配规则,优先匹配最具体的类型,但由于 B 中没有 show (D),因此向上转型为 A)

总结

向上转型是面向对象编程中实现多态性的重要手段。它允许子类对象被当作父类对象来处理,从而提高了代码的灵活性和可维护性。通过向上转型,可以在不修改现有代码的情况下,扩展程序的功能。

相关推荐
Auc248 分钟前
Java 原生实现代码沙箱(OJ判题系统第1期)——设计思路、实现步骤、代码实现
java·开发语言·python
赵和范15 分钟前
C++:求分数序列和
开发语言·c++·算法
oioihoii17 分钟前
C++23 中的 views::chunk:深入探索与应用
开发语言·python·c++23
livemetee18 分钟前
netty单线程并发量评估对比tomcat
java·tomcat·netty
lyrhhhhhhhh19 分钟前
Spring框架(1)
java·后端·spring
cs82198483121 分钟前
QT 解决msvc fatal error C1060: 编译器的堆空间不足
开发语言·qt
熊猫的反手凶变直线24 分钟前
Java-Lambda 表达式
java·开发语言·windows·笔记
在成都搬砖的鸭鸭24 分钟前
【Go底层】http标准库服务端实现原理
开发语言·http·golang
Super_man5418826 分钟前
k8s之service解释以及定义
java·开发语言·云原生·容器·kubernetes
fie888929 分钟前
Java中的控制流语句:if、switch、for、foreach、while、do-while
java·开发语言·python