目录
一、前言
继续我们面向对象篇的学习,今天学习的是继承,这部分特别容易迷,多态是重点也是难点部分。概念了解一下即可,关键的会使用它。如果想要快速了解多态的用法,就直接看第五章。第五章是一个综合案例的全套源代码。
如果看着有些困难,不妨去看一下博主的另一篇文章。
java继承http://t.csdnimg.cn/2ZyyD
二、认识多态
多态(Polymorphism)是面向对象编程中一个重要的概念,它允许在不同类的对象上执行相同的操作,但具体执行的操作可能会有所不同。在Java中,多态性通过方法重写(Override)和方法重载(Overload)实现。
方法重写(Override):
- 方法重写指子类重新定义或实现其父类的方法,方法名、参数列表和返回类型必须与父类方法完全一致。
- 当子类对象通过父类引用调用被重写的方法时,实际执行的是子类的方法,而不是父类的方法。
- 这种机制允许不同的子类对象表现出各自的行为,而不需要修改调用这些方法的代码。
方法重载(Overload):
- 方法重载指在同一个类中,可以定义多个方法名相同但参数列表不同(参数类型、个数或顺序)的方法。
- 编译器根据方法调用时传入的参数类型来决定具体调用哪个方法。
- 方法重载并不是多态的实现方式,但它提供了一种静态的多态,即在编译阶段就确定了调用的方法。
示例:
Person类(父类)
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println(name+","+age);
}
}
Administrator(子类)
public class Administrator extends Person{
@Override
public void show() {
System.out.println("管理员的信息为:"+getName()+getAge());
}
}
Student(子类)
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:"+getName()+getAge());
}
}
Teacher(子类)
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:"+getName()+getAge());
}
}
Text(测试类)
public class Text {
public static void main(String[] args) {
Student s=new Student();
s.setName("王同学");
s.setAge(18);
Teacher t=new Teacher();
t.setName("王老师");
t.setAge(30);
Administrator a=new Administrator();
a.setName("王管理员");
a.setAge(40);
register(s);
register(t);
register(a);
}
public static void register(Person p){
p.show();
}
}
运行结果
示例解释
类的定义:
Text
类是程序的入口,其中包含了main
方法。Student
,Teacher
,Administrator
类都是Person
类的子类,它们分别表示学生、老师和管理员,都继承自Person
类。
对象的创建和初始化:
Student s = new Student();
创建了一个Student
类型的对象s
,并设置了其姓名和年龄。Teacher t = new Teacher();
创建了一个Teacher
类型的对象t
,并设置了其姓名和年龄。Administrator a = new Administrator();
创建了一个Administrator
类型的对象a
,并设置了其姓名和年龄。
方法调用:
register(s);
,register(t);
,register(a);
分别调用了register
方法,并传入不同类型的对象作为参数。register
方法的参数类型为Person
,因此可以接受Student
,Teacher
,Administrator
类型的对象,这展示了多态的应用。- 在
register
方法内部,调用了Person
类的show
方法。由于子类Student
,Teacher
,Administrator
都重写了Person
类的show
方法,因此实际上调用的是各自子类的show
方法。
多态的体现:
- 多态使得在
register
方法中可以使用Person
类型的参数,而实际传入的可以是其任何子类的对象。在运行时,根据实际对象的类型确定调用哪个子类的方法,这样可以根据需要处理不同类型的对象,而无需改变方法的定义。
三、多态中调用成员的特点
在Java中,多态性主要通过方法的动态绑定(也称为运行时多态)来实现。这种特性使得在运行时根据对象的实际类型来确定调用的方法,而不是在编译时确定。
动态绑定:
- 当通过父类引用调用子类对象的方法时,实际执行的是子类重写(覆盖)的方法。例如,如果父类有一个方法,子类覆盖了这个方法,那么当通过父类类型的引用调用这个方法时,实际执行的是子类的版本。
方法的重写(Override):
- 子类可以通过重写父类的方法来提供特定于子类的实现。当父类的方法在子类中被重写后,通过父类引用调用该方法时,会调用到子类的方法实现。
成员变量的访问:
- 多态情况下,成员变量不具备多态性。即使子类覆盖了父类的成员变量,通过父类引用访问的仍然是父类的成员变量。这是因为成员变量的访问在编译时就已经确定了,而不会等到运行时才决定。
静态方法和私有方法:
- 静态方法和私有方法不具备多态性。静态方法是在编译时就确定的,因此不会被动态绑定,而是根据引用类型来确定调用的方法。私有方法也类似,子类中的同名私有方法不会覆盖父类的私有方法。
向上转型:
- 将子类实例赋给父类引用,称为向上转型。通过向上转型,可以实现对不同子类对象的统一处理,提高代码的灵活性和可扩展性。
运行时类型识别(Instanceof):
- 可以使用
instanceof
运算符来判断一个对象的实际类型,这在多态中尤为有用,可以根据需要进行类型检查和转型操作。
示例:
public class text1 {
public static void main(String[] args) {
Fu f=new adog();
Fu f1=new bdog();
//调用成员变量
System.out.println(f.name);
System.out.println(f1.name);
//调用方法
f.show();
f1.show();
}
}
class Fu{
String name="Fu";
public void show()
{
System.out.println("Fu--show");
}
}
class adog extends Fu{
String name="adog";
@Override
public void show()
{
System.out.println("adog--show");
}
}
class bdog extends Fu{
String name="bdog";
@Override
public void show()
{
System.out.println("bdog--show");
}
}
类的结构:
text1
类是程序的入口,包含了main
方法。在main
方法中,创建了两个Fu
类型的引用,分别指向adog
和bdog
对象。
对象的创建:
Fu f = new adog();
创建了一个adog
对象,并用Fu
类型的引用f
指向它。这是向上转型的示例,父类引用指向子类对象。Fu f1 = new bdog();
创建了一个bdog
对象,并用Fu
类型的引用f1
指向它。
成员变量访问:
- 在Java中,成员变量不具备多态性。无论引用是什么类型,成员变量的访问都是由引用的类型决定的,而不是对象的实际类型。
Fu
类中有一个name
成员变量,初始化为"Fu"
。adog
类和bdog
类分别有自己的name
成员变量,但是它们并不会覆盖父类Fu
的name
成员变量,而是在各自的类中定义了新的name
变量。
方法的调用:
- 在多态的情况下,调用的方法由对象的实际类型决定。
f.show();
调用的是adog
类中重写的show
方法,输出"adog--show"
。f1.show();
调用的是bdog
类中重写的show
方法,输出"bdog--show"
。
总结:
- 在成员变量访问中,引用类型决定了访问的成员变量,而方法调用则是根据对象的实际类型决定的(多态)。
- 成员变量在多态中表现为静态绑定,而方法在多态中表现为动态绑定(运行时绑定)。
- 成员变量看父类自己的,成员方法看是new的哪个对象。
四、多态的优势和弊端
优势:
灵活性和扩展性:
- 多态允许使用父类的引用来引用子类的对象,这样可以统一处理不同子类对象,提高了代码的灵活性和可扩展性。通过添加新的子类,而不需要修改现有的代码,可以轻松地扩展程序的功能。
简化代码:
- 多态可以使代码更加简洁,通过统一的接口来操作不同对象,减少了重复的代码,提高了代码的可读性和维护性。
可替换性:
- 可以随时替换具体的子类对象,而无需修改调用这些对象的代码。这种可替换性使得在运行时动态地改变程序的行为成为可能。
接口和抽象类的应用:
- 多态性使得接口和抽象类能够更好地发挥作用。通过接口和抽象类定义统一的规范,具体的子类可以按照自己的方式实现这些规范,从而提高了代码的可复用性和可维护性。
代码可读性和可理解性:
- 使用多态性可以使代码更加清晰和易于理解。通过使用父类类型的引用来操作对象,可以更专注于对象的行为和功能,而不必关注对象的具体类型。
弊端:
运行时性能开销:
- 在运行时,系统需要动态地确定对象的类型,然后调用相应的方法。这种动态绑定的过程可能会导致一些性能开销,尤其是在需要频繁调用的地方。
隐藏对象的实际类型:
- 多态性有时会隐藏对象的实际类型,这可能导致代码阅读和调试时的困惑。特别是当对象的具体类型在运行时才能确定时,理解代码的行为可能会更具挑战性。
可能引发运行时异常:
- 如果没有正确处理多态的边界条件和对象状态,可能会导致运行时异常,如空指针异常或类型转换异常。这需要在设计和实现中格外注意。
复杂性增加:
- 如果过度使用多态,可能会导致代码结构变得复杂,不易于理解和维护。因此,需要在设计中平衡使用多态性和明确的类型设计。
以上述代码为例,在子类中添加了一个特有的方法,因为父类中不存在,所以无法调用。
解决方法:
完整代码:
//解决方法,将f强转为adog
if(f instanceof adog a){
a.adogshow();
}else if(f instanceof bdog b){
b.bdogshow();
}else{
System.out.println("没有这个类型,无法转换");
}
instanceof关键字
public void bdogshow()
{
System.out.println("bdog--bdogshow");
}
为了代码的可读性,又在bdog类中添加了bdogshow方法。
注:不能转换成其他类型,容易报错。
五、多态的综合练习
写多态综合练习的时候,要将每一个类进行单独打包,Text测试类中不要有类的定义。
案例:饲养员喂动物
结果展示:
Animal类
package com.von.day11.text7;
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//共有方法
public void eat(String something)
{
System.out.println("动物吃"+something);
}
}
Cat类
package com.von.day11.text7;
public class Cat extends Animal{
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的猫吃"+something);
}
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
Dog类
package com.von.day11.text7;
public class Dog extends Animal{
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的狗抱住"+something+"猛吃");
}
public void lookHome(){
System.out.println("狗看家");
}
}
Person类
package com.von.day11.text7;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//饲养狗
public void keepPet(Dog dog, String something)
{
System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
dog.eat(something);
}
//饲养猫
public void keepPet(Cat cat, String something)
{
System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
cat.eat(something);
}
}