JAVA多态——向上转型
之前在写多态的博客时,向上转型方面的知识点没有总结。
首先再总结一下多态。
多态
多态是面向对象编程中的一个重要概念,允许不同类的对象对同一消息做出不同的响应。多态性主要通过继承和接口实现,使得代码更加灵活和可扩展。
多态的实现方式
方法重载(Overloading)
方法重载是指在同一个类中定义多个同名方法,但这些方法的参数列表不同(参数类型、数量或顺序不同)。编译器根据调用时传递的参数来决定调用哪个方法。
class Calculator {int add(int a, int b) {return a + b;}double add(double a, double b) {return a + b;}
}
方法重写(Overriding)
方法重写是指子类重新定义父类中已有的方法。子类对象调用该方法时,会执行子类中的实现,而不是父类中的实现。
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void sound() {System.out.println("Dog barks");}
}
接口实现
接口定义了一组方法签名,不同的类可以实现同一个接口,并提供不同的实现。通过接口引用调用方法时,实际执行的是具体实现类中的方法。
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示例,展示了向上转型的使用:
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)的典型体现。
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
)动态调用。
向上转型的特性
变量的类型是父类类型,因此只能访问父类中定义的方法和属性。子类特有的方法和属性在向上转型后无法访问。
只能访问父类定义的成员:
A ab = new B();
ab.name = "张三"; // A类中定义的属性
// ab.sex = "男"; // 编译错误:A类中没有sex属性
重写方法优先调用子类实现:
// B类重写了run()方法
ab.run(); // 输出:"B跑的很快"
无法直接调用子类特有的方法:
// 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 方法。
列题:
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)
总结
向上转型是面向对象编程中实现多态性的重要手段。它允许子类对象被当作父类对象来处理,从而提高了代码的灵活性和可维护性。通过向上转型,可以在不修改现有代码的情况下,扩展程序的功能。