Skip to content

Java 基础 - 面向对象

本文主要介绍Java OOP 面向对象基础和相关类图

三大特性

封装

利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。

优点:

  1. 安全性增强: 封装可以隐藏实现细节,防止外部直接访问对象的内部数据,提高了安全性。
  2. 模块化: 封装支持将功能划分为模块,使得代码更易维护、调试和理解。
  3. 降低耦合度: 封装可以减少对其他类的依赖,降低了系统的耦合度,提高了代码的灵活性。
  4. 代码复用: 封装将相关的属性和方法组织在一起,便于复用,并支持继承和多态等特性。

举例:

java
public class BankAccount {
    // 封装私有属性
    private String accountNumber;
    private double balance;

    // 构造方法
    public BankAccount(String accountNumber, double balance) {
        this.accountNumber = accountNumber;
        this.balance = balance;
    }

    // 封装方法
    public void deposit(double amount) {
        // 实现存款操作,可能涉及复杂的业务逻辑
        this.balance += amount;
    }

    public void withdraw(double amount) {
        // 实现取款操作,可能涉及复杂的业务逻辑
        if (amount <= balance) {
            this.balance -= amount;
        } else {
            System.out.println("余额不足");
        }
    }

    // 提供对私有属性的访问方法(Getter)
    public String getAccountNumber() {
        return accountNumber;
    }

    public double getBalance() {
        return balance;
    }
}

在上述例子中,BankAccount 类封装了账户的属性和相关操作。私有属性 accountNumberbalance 只能通过公共的方法进行访问,从而隐藏了内部细节。

继承

继承(Inheritance) 是面向对象编程中的一个核心概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以拥有父类的特性,并且可以在此基础上添加新的特性或修改已有的特性。

继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。

继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型

优点:

  1. 代码复用: 继承可以使子类重用父类的代码,减少代码冗余,提高了代码的可维护性。
  2. 扩展性: 可以通过在子类中添加新的属性和方法,扩展父类的功能,使代码更加灵活。
  3. 维护性: 在父类中修改代码会影响所有子类,从而减小了维护成本。
  4. 多态: 继承是实现多态的基础,子类对象可以被当做父类对象使用,提高了程序的灵活性。

举例:

java
// 父类
class Animal {
    String name;

    // 构造方法
    public Animal(String name) {
        this.name = name;
    }

    // 方法
    void eat() {
        System.out.println(name + " is eating.");
    }
}

// 子类
class Dog extends Animal {
    // 子类可以继承父类的属性和方法,并可以添加新的属性和方法

    // 构造方法
    public Dog(String name) {
        // 使用 super 调用父类的构造方法
        super(name);
    }

    // 新的方法
    void bark() {
        System.out.println(name + " is barking.");
    }
}

// 使用继承的例子
public class Main {
    public static void main(String[] args) {
        // 创建 Dog 对象
        Dog myDog = new Dog("Buddy");

        // 调用继承自父类的方法
        myDog.eat();

        // 调用子类新增的方法
        myDog.bark();
    }
}

在上述例子中,Dog 类继承了 Animal 类的属性和方法,同时添加了新的方法 bark。通过继承,Dog 类可以重用 Animal 类的代码,实现了代码复用和扩展。

类图

UML(Unified Modeling Language) 是一种用于软件系统设计和建模的标准化建模语言,其中类图是UML中最常用的一种图表之一。类图用于描述系统中的类、对象及它们之间的关系,提供了对系统结构的可视化表示。

泛化关系 (Generalization)

泛化关系(Generalization) 是UML类图中的一种关系,用于表示类之间的继承关系,在 Java 中使用 extends 关键字。在继承关系中,一个类(称为子类或派生类)可以继承另一个类(称为父类或基类)的属性和方法。泛化关系用一条带空心三角形的线表示,箭头指向父类。

  1. 箭头方向:箭头指向父类,表示子类继承自父类。子类是一种特殊化的父类,拥有父类的所有特征和行为,并且可以额外定义自己的特征和行为。
  2. 标签:通常在箭头的中间或旁边标注继承关系的名称,用来描述子类与父类之间的关系。
  3. 继承关系
    • 子类继承父类的所有公共(public)和受保护(protected)的属性和方法。
    • 子类可以重写(override)继承的方法,以满足自身的需求。
  4. 抽象类:父类可以是抽象类,表示不能被实例化的类,而只能被子类继承。抽象类通常包含至少一个抽象方法,子类必须实现这些抽象方法。
泛化关系-Car实现Vehicle
Car类继承自Vehicle类
sh
@startuml

class Vehicle {
  - fuel: String
  + start(): void
  + stop(): void
}

class Car {
  - brand: String
  + drive(): void
}

Vehicle <|-- Car

@enduml

在这个示例中,Car类继承自Vehicle类。Vehicle类是一个通用的交通工具,而Car类是Vehicle的特殊化,它继承了Vehicle的属性和方法,并且可以添加自己的特有属性和方法,比如branddrive。箭头指向Vehicle,表示CarVehicle的子类。

实现关系 (Realization)

实现关系(Realization) 是UML类图中的一种关系,用于表示类与接口之间的关联,在 Java 中使用 implements 关键字。在实现关系中,一个类可以实现一个或多个接口,从而表明它遵循了这些接口定义的规范,并实现了接口中声明的方法。

  1. 箭头方向:箭头指向接口,表示类实现了该接口。接口是一种抽象规范,而类通过实现接口来提供具体的实现。
  2. 虚线:实现关系通常用虚线表示,以区别于实线的关联关系。
  3. 标签:通常在箭头的中间或旁边标注实现关系的名称,以描述类与接口之间的关系。
  4. 接口:接口是一种纯抽象的类,它只包含方法的签名而没有具体实现。通过实现接口,类承诺提供这些方法的具体实现。
实现关系-Circle实现Shape
实现关系-Circle实现Shape
sh
@startuml

class Shape {
  + draw(): void
}

class Circle {
  + radius: double
  + draw(): void
}

Shape <|.. Circle

@enduml

在这个示例中,Circle类实现了Shape接口。接口Shape定义了一个draw方法,而Circle类通过实现Shape接口,提供了draw方法的具体实现。箭头指向Shape,表示Circle类实现了Shape接口。通过这种方式,可以确保Circle类包含了Shape接口中定义的方法。

聚合关系 (Aggregation)

聚合关系(Aggregation) 是 UML 类图中用于表示整体与部分之间的关系的一种关系。在聚合关系中,一个类可以包含另一个类的对象,但它们之间的关系不是强依赖关系。聚合关系通常用一条带空心菱形的线表示。

聚合关系-Department包含Employee
聚合关系-Department包含Employee
sh
@startuml

class Department {
  - name: String
}

class Employee {
  - id: int
  - name: String
}

Department *-- "0..*" Employee : contains

@enduml

在这个例子中,Department类和Employee类之间建立了聚合关系。关系使用 *-- 表示,而 0..* 表示一个 Department 可以包含零个或多个 Employee。带有空心菱形的线表示聚合关系,其中箭头指向整体(Department),而菱形指向部分(Employee)。这表示 Department 包含了 Employee,但它们之间的关系是松散的,Employee 对象可以属于零个或多个 Department

关联关系 (Association)

关联关系(Association) 表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。

  1. 实线连接:类之间的关联关系通常用实线表示,连接两个类的名称之间。
  2. 角色:在关联线的两端,可以标注角色,表示每个类在关联中的作用或角色。
  3. 多重性:表示一个类的实例与另一个类的实例之间的数量关系。常见的多重性包括 1(一个)、*(多个)、0..1(零个或一个)、0..*(零个或多个)等。
关联关系-Person关联Address
关联关系-Person关联Address
sh
@startuml

class Person {
  - id: int
  - name: String
}

class Address {
  - street: String
  - city: String
}

Person -- Address : has

@enduml

在这个示例中,PersonAddress之间建立了关联关系。关联关系使用实线表示,并使用关联名称 has。这表示每个 Person 对象都有一个相关联的 Address 对象,而每个 Address 对象也可以与多个 Person 对象相关联。

依赖关系 (Dependency)

依赖关系(Dependency) 是UML类图中一种表示类之间关系的关系。它表示一个类的变化可能会影响到另一个类的情况。依赖关系通常用虚线箭头表示,箭头的方向指向被依赖的类。

  1. 虚线箭头连接:依赖关系使用虚线箭头连接依赖的类,箭头的方向指向被依赖的类。
  2. 依赖方向:如果类 A 依赖于类 B,表示类 A 中的变化可能会影响到类 B。在箭头指向的类是被依赖的类。
  3. 例子:依赖关系通常体现为一个类使用另一个类的实例、参数、方法等。当一个类的方法使用到另一个类的对象时,就形成了依赖关系。
依赖关系-Driver依赖Car
依赖关系-Driver依赖Car
sh
@startuml

class Car {
  + drive(): void
}

class Driver {
  + operateVehicle(vehicle: Car): void
}

Driver --> Car : depends

@enduml

在这个示例中,Driver类依赖于Car类。Driver类中的operateVehicle方法接受一个Car对象作为参数,表明Driver类依赖于Car类。依赖关系用 depends 标签表示。这表示Driver类的变化可能会受到Car类的影响。