重启程序猿之《Java 多态深度讲解》

《Java 多态深度讲解》

一、核心观点
1.1 多态:Java 面向对象编程的关键特性
多态在 Java 编程中占据着核心地位。它允许同一类对象在不同情况下表现出不同的形态和行为,为开发者提供了极大的灵活性。例如,通过方法重载和方法重写两种形式,实现了同一方法名在不同类中的不同实现。在 Java 中,多态实现通过继承、接口和抽象类等机制来实现,使得程序可以根据不同的对象类型自动调用相应的方法,极大地提高了代码的可扩展性和可维护性。
1.2 多态驱动高效编程与代码复用
多态对提高编程效率和促进代码复用起着重要作用。通过多态机制,同一个类型的不同对象可以表现出不同的行为,使得程序的调用方式更加灵活。例如,在一个图形绘制系统中,可以定义一个抽象的图形类,然后派生出圆形、矩形等具体的图形类。在绘制图形时,只需要使用图形类的引用,而不需要关心具体的图形类型,就可以实现对不同图形的绘制。这样可以大大减少代码的重复编写,提高编程效率。同时,多态还可以通过抽象类和接口实现代码的复用。在编写抽象类和接口时,可以定义一些通用的方法和属性,这些方法和属性可以被具体的子类继承和实现,从而减少代码的重复编写。
1.3 多态应用助力构建强大软件系统
多态在构建大型软件系统中具有关键价值。它可以提升系统的稳定性和可维护性。在大型软件系统中,不同的模块之间可能存在复杂的交互关系。通过多态机制,可以使用抽象的接口或父类来定义模块之间的交互方式,而不需要关心具体的实现类。这样可以降低模块之间的耦合度,提高系统的稳定性和可维护性。例如,在一个企业级应用系统中,可以定义一个数据访问接口,然后根据不同的数据库类型实现不同的具体数据访问类。在使用数据访问接口时,只需要关心接口提供的方法,而不需要关心具体的数据库类型,这样可以方便地切换数据库,提高系统的可维护性。同时,多态还可以提高系统的可扩展性。在系统需要添加新的功能时,只需要添加新的类和方法,而不需要修改已有的代码,从而提高系统的可扩展性。
二、多态概念解析
2.1 多态的定义与本质
2.1.1 多态的基本概念
多态,即同一行为具有多种表现形式的能力。在 Java 中,这体现为同一接口的不同实例可以有不同的操作。例如,假设有一个图形绘制的接口,其中定义了一个绘制方法draw()。不同的图形类,如圆形、矩形、三角形等,都实现了这个接口,并各自实现了draw()方法。当调用这个方法时,根据具体的图形对象,会执行不同的绘制操作,这就是多态的一种表现。
2.1.2 多态的本质特征
多态存在三个必要条件:继承、重写和父类引用指向子类对象。首先,继承是多态的基础,子类继承父类的属性和方法,为多态提供了可能。例如,有一个父类Animal,子类Dog和Cat继承自Animal。子类可以继承父类的方法,如voice()方法,同时也可以重写这个方法以实现自己特定的行为。其次,重写是多态的关键,子类通过重写父类的方法,实现不同的行为。比如,Dog类和Cat类重写了Animal类的voice()方法,分别输出 “汪汪汪” 和 “喵喵喵”。最后,父类引用指向子类对象使得多态得以实现。例如,可以定义一个Animal类型的引用变量,然后指向一个Dog对象或Cat对象。通过这个引用变量调用方法时,会根据实际指向的对象类型来确定执行哪个子类的方法。
2.2 多态的类型
2.2.1 方法重载的多态性
方法重载是编译时多态,发生在同一类中。方法名相同但参数列表不同,可以体现在参数的类型、个数或者顺序上。例如,一个类中可能需要进行不同类型数据的格式化输出,可以通过方法重载来实现:

public class Formatter {
public void format(String text) {
System.out.println(“格式化文本:” + text);
}
public void format(int number) {
System.out.println(“格式化数字:” + number);
}
public void format(double number) {
System.out.println(“格式化小数:” + number);
}
}
2.2.2 方法重写的多态性
方法重写是运行时多态,子类重写父类方法,通过父类引用调用根据实际对象类型确定方法。例如,有父类Animal和子类Dog,Dog重写了Animal的voice()方法。当使用父类引用指向Dog对象时,调用voice()方法会执行Dog类中的重写方法。
2.2.3 接口实现的多态性
一个类实现接口,通过接口引用指向实现类对象实现多态。例如,定义一个接口Shape:

interface Shape {
void draw();
}
然后有不同的类实现这个接口,如Circle类和Rectangle类:

class Circle implements Shape {
@Override
public void draw() {
System.out.println(“Drawing a circle.”);
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println(“Drawing a rectangle.”);
}
}
可以通过接口引用调用不同实现类的方法,展示多态性。
2.2.4 抽象类和继承的多态性
通过继承抽象类,子类实现抽象方法体现多态。例如,定义一个抽象类Employee:

abstract class Employee {
public abstract void work();
}
子类Programmer继承Employee并实现work()方法:

class Programmer extends Employee {
@Override
public void work() {
System.out.println(“小明在敲代码”);
}
}
2.2.5 Lambda 表达式和函数式接口的多态性
Java 8 引入的 Lambda 表达式和函数式接口对多态提供了支持。例如,定义一个函数式接口AnimalSound:

@FunctionalInterface
interface AnimalSound {
void makeSound();
}
然后可以通过 Lambda 表达式或方法引用实现多态:

class Dog implements AnimalSound {
@Override
public void makeSound() {
System.out.println(“Dog barks”);
}
}
class Cat implements AnimalSound {
@Override
public void makeSound() {
System.out.println(“Cat meows”);
}
}
public class LambdaPolymorphism {
public static void main(String[] args) {
makeAnimalSound(new Dog()::makeSound);
makeAnimalSound(()->System.out.println(“Generic animal sound”));
makeAnimalSound(new Cat()::makeSound);
}
public static void makeAnimalSound(AnimalSound animalSound) {
animalSound.makeSound();
}
}
三、多态应用场景展示
3.1 多态数组的应用
多态数组是指数组元素可以是不同类型的对象,但它们具有共同的父类或实现了共同的接口。例如,以下代码展示了多态数组的应用:

Person[] person = new Person[5];
person[0] = new Person(“Alice”, 30);
person[1] = new Student(“Bob”, 20, 90);
person[2] = new Student(“Charlie”, 21, 85);
person[3] = new Teacher(“David”, 40, “Math”);
person[4] = new Teacher(“Eve”, 35, “Science”);
for (Person p : person) {
System.out.println(p.say());
if (p instanceof Student) {
((Student) p).study();
}
if (p instanceof Teacher) {
((Teacher) p).teach();
}
}
在这个例子中,Person是父类,Student和Teacher是子类。通过创建一个Person类型的数组,可以存储不同类型的子类对象。在遍历数组时,由于动态绑定机制,会根据实际的对象类型调用相应的方法。这种多态数组的应用在很多场景中非常有用,比如在一个学校管理系统中,可以使用一个数组来存储不同类型的人员对象,然后根据对象的类型执行不同的操作。
3.2 多态参数的应用
多态参数在设计方法时具有很大的优势。它允许方法接受不同类型的对象作为参数,而不关心具体的对象类型,只关注父类类型。例如,在一个动物喂食的方法中,可以使用多态参数来接受不同类型的动物对象:

public class AnimalFeeder {
public void feed(Animal animal) {
System.out.println("Feeding " + animal.getName());
animal.eat();
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println(“Dog eats bones.”);
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println(“Cat eats fish.”);
}
}
在这个例子中,AnimalFeeder类的feed方法接受一个Animal类型的参数,可以接受任何继承自Animal类的对象。这样,无论传入的是Dog对象还是Cat对象,都可以调用相应的eat方法。这种多态参数的应用使得方法更加通用和灵活,可以适应不同的对象类型。
3.3 常用类中的多态应用
在 Java 的常用类中,多态也有很多应用。例如,集合的add方法可以添加任意类型的数据,这是因为Object是所有数据类型的父类。在增强for循环遍历集合时,可以使用Object类遍历所有元素。另外,String类中的valueOf(Object obj)方法也体现了多态的应用。这个方法可以接受任何类型的对象作为参数,并返回一个字符串表示。例如:

Object obj = new Integer(10);
String str = String.valueOf(obj);
System.out.println(str);
在这个例子中,valueOf方法接受一个Object类型的参数,可以接受任何类型的对象,并返回一个字符串表示。这种多态的应用使得代码更加通用和灵活,可以适应不同的对象类型。
3.4 HttpServletRequest 中的多态体现
在 Web 开发中,HttpServletRequest接口的实现也体现了多态的应用。RequestFacade实现了HttpServletRequest接口,Tomcat 自动把RequestFacade封装到HttpServletRequest中。这样,在代码中可以使用HttpServletRequest类型的引用变量来指向不同的实现类对象,从而实现多态的效果。例如:

public class ServletExample extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getMethod();
System.out.println("Request method: " + method);
}
}
在这个例子中,doGet方法接受一个HttpServletRequest类型的参数,可以接受不同的实现类对象。这样,在代码中可以根据不同的请求对象执行不同的操作。
3.5 数据库驱动中的多态表现
在数据库编程中,多态也有很多应用。Java 定义了JDBC Driver接口,不同的数据库开发商按照规范实现该接口。这也是多态的一种表现形式,一个接口对应不同的实现类。例如,在连接 MySQL 数据库和 SQL Server 数据库时,可以使用不同的数据库驱动类:

// Mysql 注册 JDBC 驱动程序
Class.forName(“com.mysql.cj.jdbc.Driver”);
// SqlServer 注册 JDBC 驱动程序
Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”);
在这个例子中,不同的数据库驱动类实现了JDBC Driver接口,可以根据不同的数据库类型选择不同的驱动类。这种多态的应用使得代码更加通用和灵活,可以适应不同的数据库类型。
3.6 新增创建线程方式中的多态
在多线程编程中,多态也有很多应用。例如,FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口,所以FutureTask是继承了Runnable的类,可以当做Thread的参数。例如:

public class FutureThread implements Callable {
@Override
public Integer call() throws Exception {
int i = 0;
for (int j = 1; j <= 1000; j++) {
if (j % 2 == 1) {
i += j;
}
}
return i;
}
}
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureThread f1 = new FutureThread();
FutureTask t1 = new FutureTask<>(f1);
Thread myThread1 = new Thread(t1);
myThread1.start();
System.out.println(“1-1000 之内的奇数和是” + t1.get());
}
}
在这个例子中,FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口,所以FutureTask是继承了Runnable的类,可以当做Thread的参数。这种多态的应用使得代码更加通用和灵活,可以适应不同的线程创建方式。
四、多态的优势分析
4.1 提高代码的拓展性
在 Java 编程中,以父类类型作为方法的形式参数,可以传递不同的子类对象,这极大地提高了代码的拓展性。例如,假设有一个图形绘制的方法,其参数为一个父类类型的引用:

public class Drawer {
public void drawShape(Shape shape) {
shape.draw();
}
}
这里的Shape是一个抽象类或接口,代表各种图形的共同特征。不同的具体图形类,如圆形Circle、矩形Rectangle等,可以作为子类实现这个抽象类或接口,并各自实现draw方法。当调用drawShape方法时,可以传递不同的具体图形对象,而不需要修改方法的实现。这样,当需要添加新的图形类型时,只需要创建一个新的子类并实现Shape接口,然后就可以将其传递给drawShape方法进行绘制。这种方式使得代码具有很好的扩展性,可以轻松应对不断变化的需求。
4.2 可替换性
多态对已存在代码具有可替换性。例如,在一个动物行为模拟的程序中,有一个父类Animal和多个子类如Dog、Cat等。如果程序中已经存在对Animal类型的引用,并且在某些地方调用了Animal类的方法。当需要替换为不同的子类对象时,只需要将原来的Animal引用指向新的子类对象即可,而不需要修改调用该引用的代码。这样可以方便地进行代码维护和升级,提高了代码的可维护性和灵活性。
4.3 可扩充性
多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性以及其他特性的运行和操作。实际上,新加子类更容易获得多态功能。例如,在一个图形绘制系统中,已经实现了圆形、矩形等图形的绘制功能。如果需要添加一个新的图形类型,如三角形Triangle,只需要创建一个Triangle类继承自图形的父类或实现图形接口,并实现相应的绘制方法。这样,新的子类可以无缝地融入到现有的系统中,不会影响其他已存在类的功能。而且,由于多态的特性,在使用图形绘制功能的地方,不需要修改代码就可以自动适应新的图形类型。
4.4 接口性
多态是超类通过方法签名向子类提供共同接口,由子类完善或覆盖它而实现的。以一个数据库操作的例子来说,定义一个数据库操作接口DatabaseOperation:

interface DatabaseOperation {
void connect();
void executeQuery(String query);
void close();
}
不同的数据库实现类,如MySQLDatabase、OracleDatabase等,可以实现这个接口,并根据各自的数据库特点实现具体的方法。这样,在程序中可以使用DatabaseOperation类型的引用变量指向不同的数据库实现类对象,通过调用接口中的方法来进行数据库操作。超类(这里是接口)提供了一个共同的接口,子类根据自己的需求完善或覆盖这些方法,实现了多态性。
4.5 灵活性与简化性
多态提高了代码的使用效率,简化了应用软件的代码编写和修改过程。在一个复杂的业务系统中,可能涉及到多个不同类型的对象和操作。通过多态,可以使用统一的接口来处理不同类型的对象,而不需要为每个对象类型编写特定的代码。例如,在一个电商系统中,有不同类型的商品,如电子产品、服装、食品等。可以定义一个商品的父类Product,然后各个具体商品类继承自这个父类。在购物车管理的代码中,可以使用Product类型的引用变量来处理不同类型的商品,而不需要为每种商品类型编写单独的购物车处理代码。这样大大提高了代码的灵活性,同时也简化了代码的编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
五、多态实现案例分析
5.1 动物类多态案例
动物类多态案例是一个经典的示例,它清晰地展示了多态在继承和重写中的具体应用。
以下是动物类、猫类和狗类的具体实现:

class Animal {
private String name;
private int age;
public Animal() {}
public Animal(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 eat() {
System.out.println(“吃法”);
}
}
class Cat extends Animal {
public Cat() {}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(“猫吃鱼”);
}
}
class Dog extends Animal {
public Dog() {}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(“狗吃骨头”);
}
}
public class AnimalDemo {
public static void main(String[] args) {
// 创建猫类对象进行测试
Animal a1 = new Cat();
a1.setName(“加菲猫”);
a1.setAge(5);
System.out.println(a1.getName() + “,” + a1.getAge());
a1.eat();
// 创建狗类对象进行测试
Animal a2 = new Dog();
a2.setName(“哈巴狗”);
a2.setAge(4);
System.out.println(a2.getName() + “,” + a2.getAge());
a2.eat();
}
}
在这个案例中,我们定义了一个动物类Animal,它有成员变量name和age,以及一个eat方法。然后我们定义了猫类Cat和狗类Dog,它们都继承自动物类,并重写了eat方法。在测试类AnimalDemo中,我们创建了猫和狗的对象,并将它们赋值给动物类的引用变量。当我们调用eat方法时,根据实际的对象类型,会执行相应子类的eat方法。这就是多态的体现,同一方法在不同的对象中有不同的实现。
5.2 其他领域多态案例
除了动物类多态案例,多态在其他领域也有广泛的应用。
图形绘制场景:
在图形绘制系统中,可以定义一个抽象的图形类,然后派生出圆形、矩形、直线等具体的图形类。在绘制图形时,只需要使用图形类的引用,而不需要关心具体的图形类型,就可以实现对不同图形的绘制。
例如:

abstract class Figure {
protected int startX, startY;
protected int endX, endY;
protected Color color;
public Figure() {
this.color = radomColor();
}
public Color radomColor() {
int r = (int) (Math.random() * 256);
int g = (int) (Math.random() * 256);
int b = (int) (Math.random() * 256);
Color color = new Color(r, g, b);
return color;
}
public abstract void draw(Graphics g);
}
class Circle extends Figure {
@Override
public void draw(Graphics g) {
int x = startX < endX? startX : endX;
int y = startY < endY? startY : endY;
g.drawOval(x, y, Math.abs(endX - startX), Math.abs(endY - startY));
}
}
class Rectangle extends Figure {
@Override
public void draw(Graphics g) {
int x = Math.min(startX, endX);
int y = Math.min(startY, endY);
g.drawRect(x, y, Math.abs(endX - startX), Math.abs(endY - startY));
}
}
class Line extends Figure {
@Override
public void draw(Graphics g) {
g.drawLine(startX, startY, endX, endY);
}
}
在这个例子中,我们定义了一个抽象类Figure,它代表各种图形的共同特征。不同的具体图形类,如圆形Circle、矩形Rectangle和直线Line,可以作为子类实现这个抽象类,并各自实现draw方法。在绘制图形时,只需要使用Figure类型的引用变量指向不同的图形对象,然后调用draw方法即可。
员工薪资管理场景:
在员工薪资管理系统中,可以使用多态来实现不同类型员工的薪资计算。
例如:

abstract class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public abstract double earnings();
}
class Boss extends Employee {
private double fixedSalary;
public Boss(String name, int age, double fixedSalary) {
super(name, age);
this.fixedSalary = fixedSalary;
}
@Override
public double earnings() {
return fixedSalary;
}
}
class CommissionWorker extends Employee {
private double baseSalary;
private double commission;
public CommissionWorker(String name, int age, double baseSalary, double commission) {
super(name, age);
this.baseSalary = baseSalary;
this.commission = commission;
}
@Override
public double earnings() {
return baseSalary + commission;
}
}
在这个例子中,我们定义了一个抽象类Employee,它代表员工的共同特征。不同类型的员工,如老板Boss和提成员工Commi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值