首页
学习
活动
专区
圈层
工具
发布

复用

原创
作者头像
用户11708420
修改2025-06-24 10:08:53
修改2025-06-24 10:08:53
3090
举报

一、代码复用的意义

  • 代码复用是面向对象编程(OOP)的魅力之一。在 C 语言等面向过程语言中,复用通常是直接复制代码,效果不好;而 Java 围绕 “类” 来解决问题,可直接使用已构建和调试过的代码,避免重新开发。

二、Java 中代码复用的两种方式

组合(Composition)

  • 方式:在新类中创建现有类的对象。
  • 特点:通过这种方式复用的是现有代码的功能,而不是其形式。
  • 举例:比如新类 “汽车” 中包含 “发动机” 类的对象,通过使用 “发动机” 对象的功能来实现汽车的相关功能。

继承(Inheritance)

  • 方式:创建现有类类型的新类,采用现有类的形式,且无需在编码时改动其代码。
  • 特点:编译器会做大部分工作,是面向对象编程的重要基础之一,更多与功能相关的内容会在 “多态” 中介绍。
  • 举例:现有 “动物” 类,创建 “狗” 类继承 “动物” 类,“狗” 类就拥有了 “动物” 类的属性和方法,还可添加自己特有的属性和方法

组合的概念与实现

  • 组合的定义:在新类中放置现有类的对象引用,从而复用现有类的功能,是代码复用的一种方式。

组合语法示例:“电脑” 类组合 “主板” 和 “CPU” 类

代码语言:javascript
复制
// 主板类(被组合的类)
class Motherboard {
    private String brand;
    private String model;

    public Motherboard(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }

    public String getInfo() {
        return "主板:" + brand + " " + model;
    }
}

// CPU类(被组合的类)
class CPU {
    private String brand;
    private String model;
    private int coreCount;

    public CPU(String brand, String model, int coreCount) {
        this.brand = brand;
        this.model = model;
        this.coreCount = coreCount;
    }

    public String getInfo() {
        return "CPU:" + brand + " " + model + "(" + coreCount + "核)";
    }
}

// 电脑类(组合其他类的新类)
public class Computer {
    private String name;
    private Motherboard motherboard; // 组合Motherboard对象
    private CPU cpu; // 组合CPU对象

    // 通过构造函数初始化组合的对象
    public Computer(String name, Motherboard motherboard, CPU cpu) {
        this.name = name;
        this.motherboard = motherboard;
        this.cpu = cpu;
    }

    // 展示电脑配置的方法(复用组合对象的功能)
    public void showConfig() {
        System.out.println("电脑名称:" + name);
        System.out.println(motherboard.getInfo()); // 调用主板对象的方法
        System.out.println(cpu.getInfo()); // 调用CPU对象的方法
    }

    public static void main(String[] args) {
        // 创建被组合的对象
        Motherboard mb = new Motherboard("华硕", "ROG Z690");
        CPU cpu = new CPU("英特尔", "i9-13900K", 16);
        
        // 创建组合后的电脑对象
        Computer myPC = new Computer("游戏主机", mb, cpu);
        
        // 调用电脑对象的方法,间接使用组合对象的功能
        myPC.showConfig();
    }
}

示例解析

被组合的类(零件类)

  • MotherboardCPU是独立的类,各自封装了属性和功能(如getInfo()方法返回自身信息)。

组合的实现

  • Computer类中声明了MotherboardCPU类型的成员变量,通过构造函数将这两个对象 “组合” 成电脑的一部分。

功能复用

  • ComputershowConfig()方法通过调用motherboard.getInfo()cpu.getInfo(),复用了主板和 CPU 类的功能,而无需关心它们的内部实现。

继承的基本概念与语法

  • 继承的本质:创建新类时,若不显式继承其他类,会默认继承 Java 的根类Object。继承是面向对象语言的核心特性,允许新类(子类)复用现有类(父类)的属性和方法。
  • 语法格式:使用extends关键字声明继承关系,例如public class Detergent extends Cleanser,表示Detergent类继承自Cleanser类。

有关继承的注意点

super关键字的作用

  • 当子类重写父类方法后,super用于明确指定 “调用父类的方法”,避免与子类自身方法混淆。
  • 若不重写,子类继承父类方法,此时调用方法时编译器会隐式查找父类实现,无需显式用super

方法重写与接口扩展

  • 子类可重写父类的方法(需用@Override注解标记),以修改其行为;同时也可添加新方法,扩展接口功能。

访问权限与继承的关系

  • 父类中public修饰的方法可被所有子类访问;若父类方法为包访问权限(默认修饰符),则仅同包的子类可访问。为便于继承,通常建议父类字段设为private,方法设为public

类的main()方法

  • 每个类可独立包含main()方法用于测试,运行时仅执行命令行指定的类的main()。例如java Detergent会运行Detergentmain(),其中可显式调用父类的main()(如Cleanser.main(args))。

父类的非静态属性。

private修饰A,子类无法继承,无法直接访问,如子类.A。 但是可以通过有访问权限的函数如getA()来访问。

protected修饰B,跨包子类内部可以访问。但是跨包非子类如OutClass类里的子类实例不能访问。child.B 会报错。

public修饰C, 可以通过子类实例直接访问。(只要父类属性未被重写或隐藏)

子类操纵父类属性的前提:属性必须被子类继承

继承规则

  • 父类的private属性不会被子类继承,因此子类对象无法通过任何方式直接操纵(只能通过父类的公共方法间接操作)。
  • 其他访问修饰符(defaultprotectedpublic)的属性会被子类继承,子类对象可根据访问权限直接操纵。

属性隐藏(Hiding)场景

  • 若子类定义了与父类同名的属性(无论访问修饰符),则子类对象访问该属性时会优先使用子类自己的属性,父类属性会被 “隐藏”。
  • 此时若想操纵父类被隐藏的属性,需通过super.属性名显式访问:
代码语言:java
复制
class Parent {
    protected int field = 100;
}

class Child extends Parent {
    protected int field = 200; // 隐藏父类的field
    
    public void test() {
        System.out.println(field); // 输出200(子类自己的field)
        System.out.println(super.field); // 输出100(父类的field)
        super.field = 150; // 操纵父类的field
    }
}

三、派生类与基类

继承中的对象构成

派生类(子类)的对象包含一个基类(父类)的子对象,这个子对象就像独立的基类对象一样,被 “包装” 在派生类对象内部。从外部看,派生类对象拥有基类的接口,还可能有自己新增的方法和属性。

基类初始化的必要性与机制

  • 初始化的关键:为确保基类子对象正确初始化,必须在派生类构造函数中调用基类构造函数,这是唯一能保证基类初始化的方式。
  • Java 的自动插入机制:即使派生类没有显式编写构造函数,编译器也会自动生成一个无参构造函数,并在其中插入对基类无参构造函数的调用(super())。

使用new创建派生类的过程

1.分配内存空间

JVM 在堆内存中为派生类对象分配空间,包含:

  • 基类非静态成员的内存(如基类的int ageString name)。
  • 派生类自身非静态成员的内存。

所有非静态字段默认初始化为零值(基本类型)或null(引用类型)。

2.基类非静态成员初始化

  • 按顺序执行基类的非静态字段赋值和非静态代码块({})。

3.基类构造函数调用

  • 派生类构造函数首行隐式或显式调用基类构造函数(super(...)):
  • 若基类有无参构造函数,自动插入super()
  • 若基类只有有参构造函数,派生类必须显式用super(参数)调用,否则编译报错。
  • 执行基类构造函数中的代码,可能修改基类成员的值。

4.派生类非静态成员初始化

  • 执行派生类的非静态字段赋值和非静态代码块。

5.派生类构造函数执行

  • 执行派生类构造函数中的代码,完成对象的最终初始化(如参数赋值、方法调用等)。

基类构造函数带参数的场景

  • 当基类的构造函数包含参数(即没有无参构造函数)时,派生类必须在构造函数中显式调用基类的有参构造函数,否则编译会报错。

显式调用基类有参构造函数

  • 若基类没有无参构造函数(如Game(int i)),派生类必须在构造函数首行用super(参数)显式调用基类的有参构造函数,否则编译器报错(找不到基类无参构造函数)。

调用顺序的强制性

  • 对基类构造函数的调用(super(...))必须是派生类构造函数的第一个操作,否则编译错误。

代码语言:java
复制
class Dog extends Animal {
    private String breed;
    
    public Dog(String name, int age, String breed) {
        // 必须在首行显式调用基类有参构造函数
        super(name, age); // 调用Animal(String, int)
        this.breed = breed;
        System.out.println("Dog构造函数:品种=" + breed);
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、代码复用的意义
  • 二、Java 中代码复用的两种方式
  • 组合的概念与实现
  • 组合语法示例:“电脑” 类组合 “主板” 和 “CPU” 类
  • 示例解析
  • 继承的基本概念与语法
  • 有关继承的注意点
  • 子类操纵父类属性的前提:属性必须被子类继承
  • 继承规则:
  • 三、派生类与基类
  • 继承中的对象构成
  • 基类初始化的必要性与机制
  • 使用new创建派生类的过程
  • 基类构造函数带参数的场景
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档