0%

万事万物皆对象

面向对象编程三大特性:封装、继承、多态

面向对象编程(OOP)是一种以 “对象” 为核心的编程范式,其三大核心特性 ——封装继承多态—— 是构建灵活、可维护代码的基础。本文将从概念、实现、作用及示例等方面,详细解析这三大特性。

封装(Encapsulation)

封装是指将对象的状态(属性)和行为(方法)捆绑在一起,并通过访问控制隐藏内部实现细节,仅暴露必要的接口供外部交互。其核心思想是 “信息隐藏”,目的是保护数据完整性、降低代码耦合度。

封装的实现方式

  • 访问修饰符:通过private、protected、public和默认(包访问)修饰符控制属性和方法的可见性。
    • private:仅当前类可见(隐藏内部状态的核心);
    • protected:当前类、子类及同包类可见;
    • public:所有类可见(暴露的接口);
    • 默认(无修饰符):仅同包类可见。
  • getter/setter 方法:对私有属性(private)提供公共的 get(读取)和 set(修改)方法,在方法中可添加校验逻辑,确保数据合法。

示例:封装一个学生类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Student {
// 私有属性(隐藏内部状态)
private String name;
private int age;

// 公共构造方法(初始化对象)
public Student(String name, int age) {
this.name = name;
this.setAge(age); // 通过setter校验年龄
}

// getter方法(暴露读取接口)
public String getName() {
return name;
}

// setter方法(暴露修改接口,添加校验)
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间");
}
this.age = age;
}

// 公共方法(暴露行为)
public void study() {
System.out.println(name + "正在学习");
}
}
  • 说明nameageprivate 修饰,外部无法直接修改;通过 setAge 方法限制年龄范围,确保数据合法性;study 方法封装了 “学习” 行为,外部只需调用即可,无需关心内部实现。

封装的作用

  • 保护数据:防止外部随意修改对象状态,避免数据不一致;
  • 降低耦合:外部仅通过接口交互,内部实现修改时不影响外部调用;
  • 提高可维护性:代码职责清晰,便于调试和扩展。

继承(Inheritance)

继承是指一个类(子类)可以继承另一个类(父类)的属性和方法,并可以扩展新的属性和方法。其核心是 “代码复用”,允许子类站在父类的基础上扩展功能。

继承的实现方式

  • 关键字 extends:子类通过 extends 关键字继承父类(Java 中类仅支持单继承,即一个子类只能有一个直接父类)。
  • super 关键字:用于访问父类的属性、方法和构造器(如 super.属性super.方法()super(参数))。
  • 方法重写(Override):子类可以重写父类的方法,修改或扩展其行为(需满足 “两同两小一大”:方法名、参数列表相同;返回值类型、异常范围小于等于父类;访问修饰符权限大于等于父类)。

示例:继承与方法重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 父类:动物
public class Animal {
protected String name;

public Animal(String name) {
this.name = name;
}

// 父类的方法
public void eat() {
System.out.println(name + "在吃东西");
}
}

// 子类:狗(继承自动物)
public class Dog extends Animal {
// 子类扩展的属性
private String breed;

// 子类构造器(必须先调用父类构造器)
public Dog(String name, String breed) {
super(name); // 调用父类构造器
this.breed = breed;
}

// 重写父类的eat方法
@Override
public void eat() {
System.out.println(breed + "狗" + name + "在啃骨头");
}

// 子类扩展的方法
public void bark() {
System.out.println(name + "在汪汪叫");
}
}
  • 说明Dog 继承 Animal 后,自动拥有 name 属性和 eat 方法;通过 @Override 重写 eat 方法,使其更符合 “狗” 的行为;新增 bark 方法扩展功能。

继承的作用

  • 代码复用:避免重复编写父类已有的属性和方法;
  • 层次化设计:通过 “父类 - 子类” 形成类的层次结构(如 “生物 - 动物 - 狗”),体现事物的抽象关系;
  • 为多态奠定基础:子类重写父类方法后,父类引用可指向子类对象,实现多态。

多态(Polymorphism)

多态是指同一操作作用于不同对象时,产生不同的执行结果。其核心是 “运行期绑定”:方法的调用在编译期不确定,而是在运行时根据实际对象类型动态确定。

多态的实现条件

  • 继承:子类必须继承父类;
  • 方法重写:子类重写父类的方法;
  • 父类引用指向子类对象:通过父类类型的变量引用子类对象。

示例:多态的表现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class TestPolymorphism {
public static void main(String[] args) {
// 父类引用指向子类对象(多态的核心)
Animal animal1 = new Dog("旺财", "金毛");
Animal animal2 = new Cat("咪宝", "英短");

// 调用eat方法:编译时类型是Animal,运行时实际执行子类重写的方法
animal1.eat(); // 输出:金毛狗旺财在啃骨头
animal2.eat(); // 输出:英短猫咪宝在吃鱼

// 多态下,父类引用无法直接调用子类特有的方法(需强制类型转换)
if (animal1 instanceof Dog) {
Dog dog = (Dog) animal1; // 强制转换
dog.bark(); // 输出:旺财在汪汪叫
}
}
}

// 另一个子类:猫
class Cat extends Animal {
private String breed;

public Cat(String name, String breed) {
super(name);
this.breed = breed;
}

@Override
public void eat() {
System.out.println(breed + "猫" + name + "在吃鱼");
}
}
  • 说明animal1animal2 都是 Animal 类型的引用,但分别指向 DogCat 对象。调用 eat 方法时,实际执行的是子类重写的版本,体现了 “同一操作(eat)作用于不同对象,结果不同”。

多态的作用

  • 提高代码灵活性:通过父类引用统一接收不同子类对象,简化代码(如用 Animal 数组存储 DogCat);
  • 增强扩展性:新增子类(如 Bird)时,无需修改依赖父类的代码,只需重写方法即可;
  • 符合 “开闭原则”:对扩展开放(可新增子类),对修改关闭(不修改现有代码)。

补充:对象与内存模型

理解面向对象特性的基础是掌握对象的内存分配机制:

  • 引用与对象:变量(如 Student s)是 “引用”,存储在栈内存中,指向堆内存中的对象实例;
  • 基本类型与包装类:基本类型(intchar 等)直接存储值在栈中,包装类(IntegerCharacter)是对象,存储在堆中(通过 “装箱”Integer.valueOf(10) 和 “拆箱”intValue() 实现转换);
  • 堆与栈的区别:栈内存由 JVM 自动分配 / 释放(如方法局部变量),堆内存需 JVM 垃圾回收(如 new 创建的对象)

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10