最近发现自己对重载和多态竟然分不清楚,实在是觉得有点羞愧啊。特意写一篇记录一下,以后方便回顾。
关于详细解释多态(polymorphism)和重载(overloading)的关系我们需要深入理解面向对象编程(OOP)中他们各自的含义,应用的场景以及他们的具体实现方式。
多态
多态是OOP的核心概念之一,它允许对象以不同的方式响应相同的消息。多态提高了代码的可复用性和灵活性,使得程序更容易拓展和维护。在多态的概念下,同一个操作可以根据对象的类型产生不同的行为,通常可以分为以下两种形式:
编译时多态(Compile-time Polymorphism)
编译时多态又叫做静态多态,他的多态性在编译阶段就明确了。主要是通过方法重载和运算符重载来实现它。
方法重载(Method Overloading):
指在同一个类中存在多种同名的方法,但是这些方法的参数各不相同(例如参数的数量,类型或者循序不同)。编译器在编译的过程中,根据具体调用的方法是提供的参数列表来判断选择对应的重载方法,这就是重载的多态性。
例如:
class Printer{
void print(int value){
System.out.println("good,good,good!!!" + value);
}
void print(String value){
System.out.println("hi,hi,hi!!!" + value);
}
void print(int value,String suffix){
System.out.println("nice,nice,nice!!!" + value + suffix);
}
}
在这个Printer
类中,有三个重载的print
方法。当我们调用print()
时,编译器会根据传入参数的类型来选择适合的重载方法运行。
运算符重载 (Operator Overloading) :这种重载通常用于C++语言,在不同的类中对运算符(如+
、-
等)重新定义其含义,使其能够处理类对象。例如,在C++中可以重载+
运算符使其能够对两个自定义类进行操作。
运行时多态(Runtime Polymorphism)
运行时多态又叫动态多态,它是在运行的时候确定的,通常通过继承和方法重写来实现。在动态多态中,父类的引用指向子类的实例,这个引用可以调用子类中重写的方法。
方法重写:是指子类中定义的一个与父类中方法签名完全相同的方法,以便子类可以对这个方法进行不同的实现。当通过父类的引用来调用这个方法时,将根据引用所指向的实际对象类型来执行相应的方法版本。
例如:
class Animal {
void sound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Meow");
}
}
public class TestPolymorphism {
public static void main(String[] args) {
Animal a;
a = new Dog();
a.sound(); // 输出:Bark
a = new Cat();
a.sound(); // 输出:Meow
}
}
类图
classDiagram
class Animal {
+void sound()
}
class Dog {
+void sound()
}
class Cat {
+void sound()
}
Animal <|-- Dog
Animal <|-- Cat
class TestPolymorphism {
+main(String[] args)
- Animal a
}
TestPolymorphism ..> Animal : "a = new Dog() / a = new Cat()"
在上述例子中,通过父类Animal
的引用调用sound()
方法,但具体调用哪个实现是在运行时确定的,这就实现了运行时多态。
重载
重载是方法的多样性体现,是一种编译时多态的实现方式。它允许在同一个类中定义多个同名方法,但这些方法的参数列表(类型、数量、顺序)必须不同。它的主要优点在于可以让方法名具有统一性,简化类的接口,使代码更加直观。
参数类型不同:可以有不同的类型参数。
void display(int a); void display(String b);
参数个数不同:参数个数可以不同。
void add(int a, int b); void add(int a, int b, int c);
参数顺序不同:即使参数个数相同,但顺序不同,也可以构成重载。
void process(int a, double b); void process(double b, int a);
多态与重载的关系
重载是实现多态的一种方式,属于编译时多态。多态是一个更广泛的概念,既可以通过重载实现(编译时多态),也可以通过方法重写实现(运行时多态)。两者的主要关系和区别在于:
实现方式不同:
- 重载是通过同名但参数不同的方法实现的,这些方法在同一个类中,属于编译时多态。
- 运行时多态通过继承和方法重写来实现,通过父类的引用调用子类的重写方法,属于运行时多态。
作用时期不同:
- 重载在编译时确定,编译器会根据调用的参数选择合适的重载方法。
- 运行时多态则是在运行时决定调用哪个版本的方法。
代码扩展性:
- 重载提高了代码的灵活性和可读性,减少了对方法命名的需求,使程序员可以通过统一的方法名称来表达不同的功能。
- 运行时多态提供了更强大的扩展能力,使得程序可以根据具体的对象类型调用不同的实现,这对构建可扩展系统尤为重要。
实例对比
举个例子来说明重载和运行时多态的不同之处:
重载例子:
class MathOperation { // 重载方法:同名,但参数不同 int multiply(int a, int b) { return a * b; } double multiply(double a, double b) { return a * b; } } public class TestOverloading { public static void main(String[] args) { MathOperation operation = new MathOperation(); System.out.println(operation.multiply(4, 5)); // 输出:20 System.out.println(operation.multiply(3.5, 2.5)); // 输出:8.75 } }
运行时多态例子:
class Vehicle { void start() { System.out.println("Vehicle starting"); } } class Car extends Vehicle { @Override void start() { System.out.println("Car starting"); } } public class TestRuntimePolymorphism { public static void main(String[] args) { Vehicle v = new Car(); // 父类引用指向子类对象 v.start(); // 输出:Car starting } }
在运行时多态的例子中,虽然变量v的类型是Vehicle,但实际指向的是Car的实例,因此在运行时会调用Car类中的start方法。
总结
graph LR
OOP[面向对象编程] --> P[多态 Polymorphism]
P --> CP[编译时多态]
P --> RP[运行时多态]
CP --> MO[方法重载]
CP --> OO[运算符重载]
MO --> M1[参数类型不同]
MO --> M2[参数个数不同]
MO --> M3[参数顺序不同]
OO --> O1[C++特有]
OO --> O2[重定义运算符]
RP --> R1[通过继承实现]
RP --> R2[方法重写]
R2 --> RW1[子类重写父类方法]
R2 --> RW2[方法签名相同]
R2 --> RW3[运行时确定调用]
style OOP fill:#f9f,stroke:#333,stroke-width:2px
style P fill:#bbf,stroke:#333,stroke-width:2px
style CP fill:#ddf,stroke:#333,stroke-width:2px
style RP fill:#ddf,stroke:#333,stroke-width:2px
- 多态 (Polymorphism) 是面向对象编程的重要特性之一,体现了对象可以以不同的形式表现。它通过方法重载和重写实现,包括编译时多态和运行时多态。
- 重载 (Overloading) 是实现编译时多态的一种方式,通过在同一类中定义参数不同的同名方法来实现。
- 运行时多态通过继承和方法重写实现,使得程序能够根据实际的对象类型在运行时决定调用哪个方法版本。
重载和多态的结合可以使得面向对象程序设计既灵活又强大,而且更好地实现代码的复用和扩展。希望这回不能忘了!!!