【Java基础整理】封装、继承、抽象、接口和多态

Java面向对象编程:封装、继承、抽象、接口和多态

摘要

本文档详细介绍Java面向对象编程的五大核心概念:封装、继承、抽象类、接口和多态。通过理论阐述和代码示例,帮助读者深入理解Java面向对象编程的精髓和实际应用。


目录

  1. 封装
  2. 继承
  3. 抽象类
  4. 接口
  5. 多态

封装

定义

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。这是面向对象编程的基本原则之一。

权限修饰符

Java提供了四种访问权限修饰符来控制成员的访问级别:

修饰符 访问范围 描述
private 仅本类内部 私有的,只在本类中有效
default(默认) 同包内 包访问权限,同一包内可访问
protected 同包内 + 子类 保护的,只有子类可以调用
public 所有地方 公开的,对所有用户开放

权限修饰符使用规则

类的权限修饰符
  • 只能使用 publicdefault(默认)权限修饰符
  • public 类可以被任何地方访问
  • default 类只能在同一包内被访问
成员的权限修饰符
  • 成员变量和方法可以使用所有四种权限修饰符
  • 权限修饰符置于类的成员定义前,限定其他对象的访问权限

封装示例

java 复制代码
public class Student {
    // 私有属性,外部无法直接访问
    private String name;
    private int age;
    
    // 公共的getter方法
    public String getName() {
        return name;
    }
    
    // 公共的setter方法,可以添加验证逻辑
    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        }
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if (age > 0 && age < 150) {
            this.age = age;
        }
    }
}

继承

定义

继承 是类与类之间的关系,子类可以直接使用父类的方法和属性。在Java中使用 extends 关键字实现继承。

java 复制代码
子类 extends 父类

继承的作用

  1. 提高代码的复用性 - 子类可以重用父类的代码
  2. 建立类与类之间的关系 - 形成继承层次结构

继承使用注意事项

⚠️ 重要原则:千万不要为了获取其他类的功能、简化代码而继承!

  • 必须是类与类之间有关系或有共性才可以继承
  • 继承应该体现"is-a"关系

单继承

Java只支持单继承,不支持多继承:

java 复制代码
// ✅ 正确:单继承
class Dog extends Animal { }

// ❌ 错误:多继承(Java不支持)
// class Dog extends Animal, Pet { }

为什么不支持多继承?

  • 多继承容易带来隐患
  • 当多个父类定义相同功能但内容不同时,子类对象不确定运行哪个
  • Java通过接口的多实现来完成多继承的表示

成员变量访问

当子类中出现与父类同名的成员变量时:

关键字 访问对象 说明
this 本类对象 访问子类中的同名变量
super 父类对象 访问父类中的同名变量
java 复制代码
class Parent {
    String name = "父类";
}

class Child extends Parent {
    String name = "子类";
    
    public void showNames() {
        System.out.println("子类名称: " + this.name);    // 输出: 子类
        System.out.println("父类名称: " + super.name);   // 输出: 父类
    }
}

方法重写(Override)

当子类中出现与父类完全相同的方法时,子类对象调用该方法会运行子类方法的内容。

重载 vs 重写对比
特性 重载(Overload) 重写(Override)
英文名 Overload Override
发生位置 同一个类中 父子类之间
方法名 相同 相同
参数列表 不同 相同
返回值 可以不同 必须相同(或协变)
访问修饰符 可以不同 不能更严格
java 复制代码
class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override  // 建议使用注解
    public void makeSound() {
        System.out.println("汪汪汪");
    }
}

构造函数与继承

重要规则
  1. 子类对象初始化时,父类的构造函数也会运行
  2. 子类所有构造函数默认第一行都有隐式语句:super();
  3. 会优先访问父类中的无参构造函数
构造函数调用顺序
java 复制代码
class Parent {
    public Parent() {
        System.out.println("父类无参构造函数");
    }
    
    public Parent(String name) {
        System.out.println("父类有参构造函数: " + name);
    }
}

class Child extends Parent {
    public Child() {
        // 隐式调用 super();
        System.out.println("子类无参构造函数");
    }
    
    public Child(String name) {
        super(name);  // 显式调用父类有参构造函数
        System.out.println("子类有参构造函数");
    }
}
特殊情况处理

当父类没有无参构造函数时:

java 复制代码
class Parent {
    // 只有有参构造函数,没有无参构造函数
    public Parent(String name) {
        this.name = name;
    }
}

class Child extends Parent {
    public Child() {
        super("默认名称");  // 必须显式调用父类构造函数
    }
    
    public Child(String name) {
        super(name);
    }
}

抽象类

定义

当多个类中出现相同功能,但功能主体不同时,可以向上抽取。只抽取功能定义,而不抽取功能主体,这就形成了抽象类

abstract修饰符

修饰对象 描述 特点
抽象类 abstract修饰的类 无法被直接实例化
抽象方法 abstract修饰的方法 只有声明,没有实现

抽象类的实现

抽象类必须有子类完全重写所有抽象方法才能使用:

java 复制代码
// 抽象类
abstract class Animal {
    // 普通方法
    public void sleep() {
        System.out.println("动物在睡觉");
    }
    
    // 抽象方法
    public abstract void makeSound();
    public abstract void move();
}

// 具体实现类
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("汪汪汪");
    }
    
    @Override
    public void move() {
        System.out.println("狗在跑");
    }
}

抽象类特点

  1. abstract修饰
  2. 类中可以有抽象方法
  3. 无法被直接创建对象
  4. 可以有普通方法和成员变量
  5. 可以有构造方法(供子类调用)

抽象类使用示例

java 复制代码
abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    // 抽象方法
    public abstract double getArea();
    public abstract double getPerimeter();
    
    // 普通方法
    public void displayInfo() {
        System.out.println("形状颜色: " + color);
        System.out.println("面积: " + getArea());
        System.out.println("周长: " + getPerimeter());
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double getPerimeter() {
        return 2 * Math.PI * radius;
    }
}

接口

定义

接口 可以认为是一个特殊的抽象类,接口中的方法全部都是抽象方法。子类使用 implements 关键字来实现接口。

接口成员定义

接口中的成员有固定的修饰符:

成员类型 默认修饰符 说明
常量 public static final 必须初始化,不可修改
方法 public abstract 抽象方法,无实现
为什么是这些修饰符?
  • public:接口需要被实现,必须公开
  • static:接口不能被实例化,常量只能是静态的
  • final:接口不能有对象,常量必须是固定的
  • abstract:接口中只有抽象方法

接口定义示例

java 复制代码
interface Drawable {
    // 常量(自动加上 public static final)
    int MAX_SIZE = 100;
    
    // 抽象方法(自动加上 public abstract)
    void draw();
    void resize(int width, int height);
}

接口特点

  1. 接口不能创建对象(因为有抽象方法)
  2. 必须被子类完全实现所有抽象方法
  3. 支持多实现(一个类可以实现多个接口)
  4. 接口可以继承接口(支持多继承)

接口多实现示例

java 复制代码
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

// 类可以实现多个接口
class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("鸭子在飞");
    }
    
    @Override
    public void swim() {
        System.out.println("鸭子在游泳");
    }
}

接口继承接口

java 复制代码
interface A {
    void methodA();
}

interface B {
    void methodB();
}

// 接口可以继承多个接口
interface C extends A, B {
    void methodC();
}

// 实现类必须实现所有方法
class MyClass implements C {
    @Override
    public void methodA() { /* 实现A */ }
    
    @Override
    public void methodB() { /* 实现B */ }
    
    @Override
    public void methodC() { /* 实现C */ }
}

⚠️ 注意:多个父接口不能有不同返回值类型的同名方法。


多态

定义

多态可以理解为事物存在的多种体现形态。在Java中,父类的引用可以接收子类对象。

多态的体现

核心表现:父类的引用指向子类对象

java 复制代码
// 最简单的例子
List<String> list = new ArrayList<>();  // List是ArrayList的父接口

// 动物多态示例
Animal animal1 = new Dog();     // 父类引用指向子类对象
Animal animal2 = new Cat();     // 同一个引用类型,不同的实际对象

多态的前提

  1. 必须有继承关系(继承或实现)
  2. 必须有方法重写(这样多态使用才有意义)

多态的好处

提高程序的扩展性:前期定义父类引用,后期可以调用新的子类对象。

java 复制代码
public class AnimalManager {
    // 面向父类编程,支持所有子类
    public void makeAnimalSound(Animal animal) {
        animal.makeSound();  // 具体调用哪个取决于传入的对象类型
    }
}

// 使用时的扩展性
AnimalManager manager = new AnimalManager();
manager.makeAnimalSound(new Dog());    // 输出: 汪汪汪
manager.makeAnimalSound(new Cat());    // 输出: 喵喵喵
manager.makeAnimalSound(new Bird());   // 新增类型,无需修改代码

多态的局限性

只能访问父类成员:多态中只能使用父类的引用访问父类中的成员,无法获取子类特有成员。

java 复制代码
class Animal {
    public void eat() { System.out.println("动物吃东西"); }
}

class Dog extends Animal {
    public void eat() { System.out.println("狗吃骨头"); }
    public void bark() { System.out.println("汪汪汪"); }  // 子类特有方法
}

public class TestPolymorphism {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();     // ✅ 可以调用,运行子类重写的方法
        // animal.bark(); // ❌ 编译错误!父类引用无法调用子类特有方法
    }
}

多态中成员调用规则

方法调用规则
情况 调用规则 示例
子类重写了父类方法 调用子类方法 B子类的method1
子类没有重写 调用父类方法 A父类的method2
父类没有该方法 编译时错误 无法编译
静态方法 调用父类静态方法 A父类的static方法
详细示例
java 复制代码
// 定义父类A
class A {
    void method1() {
        System.out.println("A父类的method1");
    }
    
    void method2() {
        System.out.println("A父类的method2");
    }
    
    static void methodS() {
        System.out.println("A父类的static方法");
    }
}

// 定义子类B继承了A
class B extends A {
    @Override
    void method1() {
        System.out.println("B子类的method1");
    }
    
    void method3() {
        System.out.println("B子类的method3");
    }
    
    static void methodS() {
        System.out.println("B子类的static方法");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        A a = new B();
        a.method1();  // 输出: B子类的method1 (子类重写了)
        a.method2();  // 输出: A父类的method2 (子类没重写)
        a.methodS();  // 输出: A父类的static方法 (静态方法看引用类型)
        // a.method3(); // 编译错误!父类引用无法获取子类特有成员
    }
}
成员变量调用规则

重要 :成员变量不存在重写,无论编译还是运行都参考引用类型(父类)。

java 复制代码
class A {
    String s = "A父类";
}

class B extends A {
    String s = "B子类";
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.s);  // 输出: A父类
    }
}

多态的应用

类型转换
向上转型(自动)
java 复制代码
Animal animal = new Dog();  // 自动向上转型
向下转型(强制)
java 复制代码
Animal animal = new Dog();
Dog dog = (Dog) animal;     // 强制向下转型
dog.bark();                 // 现在可以调用子类特有方法了
安全的向下转型
java 复制代码
Animal animal = new Dog();

// 使用 instanceof 判断类型
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.bark();
} else if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    cat.meow();
}
多态实际应用示例
java 复制代码
// 图形绘制系统
abstract class Shape {
    public abstract void draw();
    public abstract double getArea();
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) { this.radius = radius; }
    
    @Override
    public void draw() { System.out.println("绘制圆形"); }
    
    @Override
    public double getArea() { return Math.PI * radius * radius; }
}

class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public void draw() { System.out.println("绘制矩形"); }
    
    @Override
    public double getArea() { return width * height; }
}

// 图形管理器 - 体现多态的威力
class ShapeManager {
    public void drawAllShapes(Shape[] shapes) {
        for (Shape shape : shapes) {
            shape.draw();  // 多态调用,具体调用哪个draw取决于实际对象类型
            System.out.println("面积: " + shape.getArea());
        }
    }
}

public class PolymorphismApplication {
    public static void main(String[] args) {
        Shape[] shapes = {
            new Circle(5.0),
            new Rectangle(4.0, 6.0),
            new Circle(3.0)
        };
        
        ShapeManager manager = new ShapeManager();
        manager.drawAllShapes(shapes);  // 多态的实际应用
    }
}

总结

Java面向对象五大特性关系图

markdown 复制代码
面向对象编程
├── 封装(Encapsulation)
│   ├── 隐藏内部实现
│   ├── 提供公共接口
│   └── 控制访问权限
├── 继承(Inheritance)
│   ├── 代码复用
│   ├── 建立类层次
│   └── 单继承限制
├── 抽象(Abstraction)
│   ├── 抽象类
│   ├── 抽象方法
│   └── 模板模式
├── 接口(Interface)
│   ├── 规范定义
│   ├── 多实现
│   └── 接口继承
└── 多态(Polymorphism)
    ├── 编译时多态(重载)
    ├── 运行时多态(重写)
    └── 类型转换

核心概念对比

概念 关键字 主要作用 特点
封装 private/protected/public 隐藏实现细节 控制访问权限
继承 extends 代码复用 单继承,is-a关系
抽象 abstract 定义规范 不能实例化
接口 interface/implements 规范约束 多实现,全抽象
多态 重写@Override 灵活扩展 父类引用指向子类对象

设计原则

  1. 封装原则:高内聚,低耦合
  2. 继承原则:遵循is-a关系,避免为了复用而继承
  3. 抽象原则:面向抽象编程,而非具体实现
  4. 接口原则:依赖抽象,而非具体
  5. 多态原则:开闭原则,对扩展开放,对修改关闭

实战建议

  1. 合理使用封装

    • 成员变量设为private
    • 提供public的getter/setter方法
    • 在setter中添加验证逻辑
  2. 正确使用继承

    • 确保是is-a关系
    • 避免为了复用而继承
    • 优先使用组合而非继承
  3. 善用抽象和接口

    • 抽象类用于有共同实现的场景
    • 接口用于定义规范和约束
    • 面向接口编程
  4. 充分利用多态

    • 参数和返回值使用父类型
    • 利用多态实现可扩展的设计
    • 合理使用类型转换

通过深入理解和实践这五大特性,能够编写出更加优雅、可维护和可扩展的Java代码。

相关推荐
行百里er2 小时前
2026:一名码农的“不靠谱”年度规划
后端·程序员·架构
全靠bug跑4 小时前
Spring Cache 实战:核心注解详解与缓存过期时间配置
java·redis·springcache
聆风吟º4 小时前
【数据结构手札】空间复杂度详解:概念 | 习题
java·数据结构·算法
计算机程序设计小李同学4 小时前
基于SpringBoot的个性化穿搭推荐及交流平台
java·spring boot·后端
是一个Bug4 小时前
50道核心JVM面试题
java·开发语言·面试
用户47949283569154 小时前
同事一个比喻,让我搞懂了Docker和k8s的核心概念
前端·后端
朱朱没烦恼yeye4 小时前
java基础学习
java·python·学习
她和夏天一样热5 小时前
【观后感】Java线程池实现原理及其在美团业务中的实践
java·开发语言·jvm
郑州光合科技余经理5 小时前
技术架构:上门服务APP海外版源码部署
java·大数据·开发语言·前端·架构·uni-app·php
篱笆院的狗5 小时前
Java 中的 DelayQueue 和 ScheduledThreadPool 有什么区别?
java·开发语言