【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代码。

相关推荐
㳺三才人子5 小时前
初探 Flask
后端·python·flask·html
星栈独行5 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
Lei活在当下5 小时前
先用起来,再理解,关于协程Coroutine应该知道的事
android·java·jvm
Java爱好狂.5 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
陈随易6 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
tongluowan0076 小时前
以ReentrantLock为例解释AQS的工作流程
java·模板方法模式·aqs·reentrantlock
装不满的克莱因瓶6 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
ltl7 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端
身如柳絮随风扬7 小时前
Java 项目打包与部署完全指南:JAR vs WAR,从构建到运行
java·firefox·jar
云烟成雨TD7 小时前
Spring AI Alibaba 1.x 系列【62】时光旅行(Time-Travel)
java·人工智能·spring