封装,继承,多态
下面我将分别对它们进行详细解释,包括其特点、具体含义以及在项目中的应用。
一、封装
1. 特点
数据隐藏:对象的内部状态(数据)对外部是不可见的,外部不能直接访问和修改。
访问控制:通过提供有限的、受控的接口(通常是公共方法)来与对象进行交互。
模块化:将数据和对数据的操作捆绑在一起,形成一个独立的单元(类),降低了代码的耦合度。
易于维护:当类的内部实现需要改变时,只要不修改其公共接口,就不会影响到依赖该类的其他代码。
2. 具体含义
封装就像使用一个遥控器来控制电视。你不需要知道电视内部的电路板、显像管是如何工作的(这些是隐藏的数据),你只需要通过遥控器上的几个按钮(公共方法,如powerOn()
, changeChannel()
)来操作电视即可。遥控器封装了电视复杂的内部细节,只为你提供简单、安全的操作界面。
在代码中,封装通常通过以下方式实现:
将成员变量(字段)设为 private:这是实现数据隐藏的关键,确保外部代码不能直接访问这些变量。
提供 public 的 getter 和 setter 方法:作为外部访问和修改私有变量的唯一途径。在这些方法中,可以添加逻辑进行数据校验、权限控制等。
示例代码:
// 一个封装良好的 User 类
public class User {// 1. 将数据成员设为 private,实现数据隐藏private String username;private String password;private int age;// 2. 提供公共的构造器和方法来操作数据public User(String username, String password, int age) {this.username = username;setPassword(password); // 通过 setter 方法设置密码,可以在这里进行加密this.age = age;}// 3. 提供公共的 getter 方法public String getUsername() {return username;}public int getAge() {return age;}// 4. 提供公共的 setter 方法,可以在其中添加逻辑public void setAge(int age) {if (age > 0) { // 数据校验this.age = age;} else {System.out.println("年龄必须大于0");}}// 密码不应该提供 getter,修改时通过加密逻辑public void setPassword(String password) {// 这里可以添加密码加密逻辑this.password = password;}
}
3. 项目中的应用
实体类:如 User, Product, Order 等。它们的内部状态(如用户名、价格、订单状态)被封装起来,只能通过特定的方法进行修改,保证了数据的完整性。
服务层:服务类封装了复杂的业务逻辑。例如,一个 OrderService 会封装创建订单、取消订单、支付订单等一系列操作,外部调用者只需调用 orderService.createOrder(),而无需关心订单创建背后数据库如何操作、如何计算折扣等细节。
配置管理:一个 AppConfig 类可以封装所有应用程序的配置信息(如数据库URL、API密钥),外部代码通过 config.getDbUrl() 来获取,而不是直接访问一个静态字符串。
二、继承
1. 特点
实体类:如 User, Product, Order 等。它们的内部状态(如用户名、价格、订单状态)被封装起来,只能通过特定的方法进行修改,保证了数据的完整性。
服务层:服务类封装了复杂的业务逻辑。例如,一个 OrderService 会封装创建订单、取消订单、支付订单等一系列操作,外部调用者只需调用 orderService.createOrder(),而无需关心订单创建背后数据库如何操作、如何计算折扣等细节。
配置管理:一个 AppConfig 类可以封装所有应用程序的配置信息(如数据库URL、API密钥),外部代码通过 config.getDbUrl() 来获取,而不是直接访问一个静态字符串。
2. 具体含义
继承就像生物学中的“父子”关系。子类(派生类)会自动拥有父类(基类)的所有特征和行为,并且可以在此基础上扩展自己的新特征和行为,或者重写父类已有的行为。
示例代码:
// 父类:动物
public class Animal {public String name;public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + " 正在吃东西。");}
}// 子类:狗,继承自动物
public class Dog extends Animal {// Dog 自动继承了 name 属性和 eat() 方法public Dog(String name) {// 必须调用父类的构造器来初始化继承的属性super(name);}// Dog 独有的方法public void bark() {System.out.println(name + " 在汪汪叫。");}// 可以重写父类的方法@Overridepublic void eat() {System.out.println(name + " 在吃狗粮。");}
}// 使用
public class Main {public static void main(String[] args) {Dog myDog = new Dog("旺财");myDog.eat(); // 调用的是重写后的方法myDog.bark(); // 调用自己的独有方法}
}
3. 项目中的应用
构建通用框架:创建一个通用的 BaseController 或 BaseService,包含所有控制器/服务共用的日志记录、异常处理、权限校验等方法。然后让具体的控制器(如 UserController, ProductController)去继承它,从而复用这些通用功能。
实体类分层:可以创建一个 BaseEntity 抽象类,包含 id, createTime, updateTime 等所有实体共有的字段。然后让 User, Product 等实体类去继承它。
处理不同类型的数据:在支付系统中,可以有一个 Payment 父类,然后有 AlipayPayment, WechatPayment, CreditCardPayment 等子类。它们都实现了 pay() 方法,但内部逻辑不同。
三、多态
1. 特点
同一接口,不同实现:同一个方法调用,作用于不同的对象,会产生不同的行为。
提高代码的灵活性和可扩展性:可以编写更通用、更灵活的代码,而无需关心具体对象是什么类型。
降低耦合度:代码依赖于抽象(如接口或抽象类),而不是具体的实现类。
2. 具体含义
多态的字面意思是“多种形态”。在编程中,它意味着一个类型的变量可以引用多种不同类型的对象,并且在调用方法时,程序会根据实际运行时的对象类型来决定调用哪个方法。
实现多态需要三个条件:
- 继承:必须有类之间的继承关系。
- 重写:子类必须重写父类的方法。
- 父类引用指向子类对象:
Parent p = new Child();
示例代码:
// 继承关系(同上)
class Animal {public void makeSound() {System.out.println("动物发出声音");}
}class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("汪汪");}
}class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("喵喵");}
}// 测试多态
public class PolymorphismDemo {public static void main(String[] args) {// 1. 父类引用指向子类对象Animal myDog = new Dog();Animal myCat = new Cat();// 2. 调用同一方法,但产生不同行为// 编译器看的是声明类型(Animal),运行时JVM看的是实际对象类型(Dog/Cat)myDog.makeSound(); // 输出: 汪汪myCat.makeSound(); // 输出: 喵喵// 创建一个可以处理所有动物的数组Animal[] animals = new Animal[3];animals[0] = new Dog();animals[1] = new Cat();animals[2] = new Animal();// 遍历并调用,代码非常通用和灵活for (Animal animal : animals) {animal.makeSound();}}
}
3. 项目中的应用
策略模式:这是多态最经典的应用场景。假设一个电商系统需要支持多种促销活动,如 CouponDiscount
(优惠券折扣)、GroupBuyDiscount
(团购折扣)等。它们都实现一个 DiscountStrategy
接口,该接口有一个 calculate()
方法。
public interface DiscountStrategy {
double calculate(double originalPrice);
}
在购物车类中,可以持有一个 `DiscountStrategy` 的引用,而不是具体的折扣类。当需要计算折扣时,只需调用 `strategy.calculate()`。切换促销活动时,只需传入不同的策略实现类即可,购物车代码完全不需要修改。
模板方法模式:定义一个算法的骨架,而将一些步骤延迟到子类中。例如,一个 DataProcessor 抽象类定义了 process() 方法,其中包含 readData(), processData(), saveData() 等步骤。readData() 和 saveData() 可以有默认实现,而 processData() 是抽象方法。具体的处理器(如 JsonDataProcessor, XmlDataProcessor)去继承并实现 processData()。
事件监听器:在GUI编程(如Java Swing)或Web开发中,按钮点击事件、鼠标移动事件等都是通过多态实现的。你为按钮添加一个 ActionListener,这个监听器就是一个对象。当点击事件发生时,框架会调用监听器对象的 actionPerformed() 方法,而不管这个监听器具体是什么类型。
依赖注入/框架设计:许多框架(如Spring)的核心思想就是依赖注入。你让业务类依赖于一个接口(如 Repository),而不是一个具体的实现(如 JdbcRepository)。在运行时,框架会注入一个具体的实现对象。这使得业务逻辑与数据访问技术完全解耦,可以轻松地在MySQL和MongoDB之间切换,而无需改动业务代码。
总结
特性 | 封装 | 继承 | 多态 |
---|---|---|---|
核心思想 | 数据隐藏,保护对象内部状态 | 代码复用,建立 is-a 关系 | 同一接口,不同实现 |
解决的问题 | 如何保护数据、降低耦合 | 如何复用代码、建立结构 | 如何提高灵活性、降低耦合 |
关系 | 独立,是OOP的基础 | 是多态实现的前提 | 是封装和继承的最终体现 |
项目中的角色 | 定义类的边界和行为 | 构建代码的骨架和层次 | 实现灵活、可扩展的业务逻辑 |
在实际项目中,这三大特性是相辅相成、密不可分的。通过封装来保护数据,通过继承来构建代码结构,最终利用多态来实现高内聚、低耦合、易于扩展和维护的软件系统。