0

0

Java中基于继承和多态实现动态ID生成策略

霞舞

霞舞

发布时间:2025-10-02 15:35:01

|

736人浏览过

|

来源于php中文网

原创

java中基于继承和多态实现动态id生成策略

本文将详细介绍如何在Java的类继承体系中,为不同子类生成符合特定规则的唯一标识符(ID)。我们将探讨如何利用抽象类、静态变量和多态性,实现ID前缀根据子类类型动态变化,同时保持ID后缀全局递增的复杂逻辑,并提供完整的代码示例和设计思路。

1. 问题背景与ID生成规则

在许多系统设计中,为不同类型的实体生成唯一且具有特定结构规则的标识符(ID)是一项常见需求。本教程将解决一个具体场景:设计一个物品(Item)的类层次结构,其中包含Flight(飞行)和Payload(载荷)两大抽象子类,以及更具体的CommercialFlight(商业航班)、IndustrialFlight(工业航班)、Person(人员)和Cargo(货物)等具体子类。所有Item都需要一个9位长的唯一ID,其生成规则如下:

  • ID长度: 所有ID均为9位数字。
  • 首位规则: ID的首位数字根据物品的具体类型而定:
    • 商业航班(CommercialFlight)以1开头。
    • 工业航班(IndustrialFlight)以2开头。
    • 人员(Person)以3开头。
    • 货物(Cargo)以4开头。
  • 后八位规则: ID的后八位数字从0开始,每创建一个新的Item(无论类型),该数值就递增1。
    • 例如:第一个创建的Item(假设是CommercialFlight)将获得ID 100000000。
    • 接着创建一个Person,它将获得ID 300000001。
    • 再创建一个Cargo,它将获得ID 400000002,以此类推。

这个规则的核心挑战在于如何将子类特有的前缀与全局递增的后缀有效结合。

2. 初始尝试及局限性分析

初学者在解决此类问题时,可能会尝试在基类的构造函数中使用this instanceof判断当前实例的类型,并为每个类型维护一个独立的计数器,如下所示:

public abstract class Item {
   protected int id;

   public Item() {
         int commercialID = 100000000; // 局部变量
         int industrialID = 200000000; // 局部变量
      if( this instanceof Commercial){ // 假设 Commercial 是具体类
         id = commercialID;
         commercialID++; // 递增的是局部变量的副本
      } else if (this instanceof Industrial) { // 假设 Industrial 是具体类
         id = industrialID;
         industrialID++; // 递增的是局部变量的副本
      }
   }
}

这种方法存在以下几个主要问题:

立即学习Java免费学习笔记(深入)”;

  1. 计数器非全局性: commercialID和industrialID是Item构造函数内的局部变量。每次创建Item实例时,它们都会被重新初始化,因此无法实现全局递增的效果。即使将它们声明为Item类的成员变量,也只会为每个Item实例维护一个独立的副本,而非所有Item共享的全局计数器。
  2. 违反开放/封闭原则: Item类需要了解所有具体的子类类型,并使用instanceof进行判断。这意味着每当添加新的子类时,Item类的构造函数都需要修改,这违反了面向对象设计的开放/封闭原则(对扩展开放,对修改封闭)。
  3. 代码冗余与耦合: 随着子类数量的增加,if-else if链会变得冗长,并且Item类与所有子类紧密耦合。

为了克服这些局限性,我们需要一种更优雅、更具扩展性的面向对象解决方案。

3. 面向对象解决方案设计

解决此问题的关键在于利用Java的抽象类静态变量多态性

3.1 核心设计思想

  1. 抽象基类 (Item): 作为所有物品的顶级父类,它负责定义ID的通用结构和生成逻辑,包括维护一个全局的递增计数器。
  2. 静态变量 (lastGeneratedId): 为了实现ID后缀的全局唯一递增,我们需要一个static类型的变量来存储上一个生成的ID后缀值。static变量属于类本身,而不是类的任何特定实例,因此所有Item实例共享同一个lastGeneratedId。
  3. 抽象方法 (getIdPrefix): 为了让每个子类定义其独特的ID前缀,而不让Item类知道所有子类的具体实现,我们可以在Item类中定义一个抽象方法getIdPrefix()。这个方法强制所有具体子类必须实现它,从而返回其自身的ID前缀。这利用了多态性,使得在Item构造函数中调用getIdPrefix()时,实际执行的是当前具体子类中实现的方法。

3.2 Item 抽象基类实现

Item类是整个ID生成机制的核心。它将包含ID的存储、全局计数器以及组合ID的逻辑。

public abstract class Item {
    protected int id; // 存储生成的ID
    protected static int lastGeneratedId = 0; // 静态变量,用于存储全局递增的后缀

    protected Item() {
        // 每次创建新的Item实例时,全局计数器递增
        lastGeneratedId++;
        // 组合ID:(前缀 * 100000000) + 后缀
        // 注意:这里100000000是为了将前缀放到9位ID的首位,后8位留给lastGeneratedId
        this.id = 100000000 * getIdPrefix() + lastGeneratedId;
        System.out.println(toString()); // 打印创建信息,便于调试
    }

    // 抽象方法:强制子类实现,返回其对应的ID前缀
    protected abstract int getIdPrefix();

    @Override
    public String toString() {
        return "New " + getClass().getName() + " created with id " + id;
    }
}

解释:

  • protected static int lastGeneratedId = 0;:这是一个静态成员变量,所有Item及其子类的实例共享同一个lastGeneratedId。它从0开始,每次有新的Item被创建时,都会递增。
  • protected Item():构造函数被声明为protected,意味着只有Item的子类可以调用它。在构造函数中,lastGeneratedId首先递增,然后通过调用抽象方法getIdPrefix()获取当前子类的特定前缀,并与递增后的lastGeneratedId组合成最终的id。
  • protected abstract int getIdPrefix();:这是一个抽象方法,它没有具体实现。任何直接或间接继承Item的非抽象子类都必须提供这个方法的具体实现,以返回该子类对应的ID前缀。

3.3 中间抽象类 (Flight, Payload)

为了更好地组织类层次结构,我们引入了Flight和Payload这两个抽象子类。它们继承自Item,但由于它们本身也是抽象的,所以不需要实现getIdPrefix()方法。它们的主要作用是提供更具体的类型分组。

Magician
Magician

Figma插件,AI生成图标、图片和UX文案

下载
// Flight 抽象子类
public abstract class Flight extends Item {
    public Flight() {
        super(); // 调用父类Item的构造函数
    }
}

// Payload 抽象子类
public abstract class Payload extends Item {
    public Payload() {
        super(); // 调用父类Item的构造函数
    }
}

3.4 具体子类实现

现在,我们来实现具体的子类。每个具体子类都需要继承其相应的抽象父类,并实现getIdPrefix()方法,返回其类型对应的ID前缀。

// 商业航班
public class CommercialFlight extends Flight {
    public CommercialFlight() {
        super(); // 调用父类Flight的构造函数,进而调用Item的构造函数
    }

    @Override
    protected int getIdPrefix() {
        return 1; // 商业航班的前缀是1
    }
}

// 工业航班
public class IndustrialFlight extends Flight {
    public IndustrialFlight() {
        super();
    }

    @Override
    protected int getIdPrefix() {
        return 2; // 工业航班的前缀是2
    }
}

// 人员
public class Person extends Payload {
    public Person() {
        super();
    }

    @Override
    protected int getIdPrefix() {
        return 3; // 人员的前缀是3
    }
}

// 货物
public class Cargo extends Payload {
    public Cargo() {
        super();
    }

    @Override
    protected int getIdPrefix() {
        return 4; // 货物的前缀是4
    }
}

4. 完整代码实现与测试

为了验证上述设计,我们创建一个ItemTester类来实例化不同类型的Item。

public class ItemTester {
    public static void main(String[] args) {
        System.out.println("--- Creating Items ---");
        new Cargo(); // 第一个Item
        new IndustrialFlight(); // 第二个Item
        new Cargo(); // 第三个Item
        new CommercialFlight(); // 第四个Item
        new IndustrialFlight(); // 第五个Item
        new Person(); // 第六个Item
        new Person(); // 第七个Item
        System.out.println("--- All Items created ---");
    }
}

运行结果示例:

--- Creating Items ---
New Cargo created with id 400000001
New IndustrialFlight created with id 200000002
New Cargo created with id 400000003
New CommercialFlight created with id 100000004
New IndustrialFlight created with id 200000005
New Person created with id 300000006
New Person created with id 300000007
--- All Items created ---

从输出结果可以看出,ID的前缀正确地根据子类类型变化,而后八位数字(从1开始,因为lastGeneratedId在构造函数中首先递增)则全局递增,完美符合了设计要求。

5. 设计优势与注意事项

5.1 设计优势

  • 多态性与扩展性: 这种设计充分利用了Java的多态性。Item类无需知道具体的子类类型,只需调用getIdPrefix()方法,实际执行的是当前子类中重写的方法。当需要添加新的Item类型时,只需创建新的子类并实现getIdPrefix()方法即可,无需修改Item基类,符合开放/封闭原则。
  • 单一职责原则: Item基类负责ID的通用生成逻辑和全局计数器管理,而每个具体子类只负责提供其自身的ID前缀。职责划分清晰。
  • 状态管理: 使用static变量lastGeneratedId确保了ID后缀的全局唯一递增,避免了重复和冲突。

5.2 注意事项

  1. 线程安全: 在多线程环境下,lastGeneratedId++操作可能存在竞态条件(race condition),导致ID生成不连续或重复。为了确保线程安全,可以使用java.util.concurrent.atomic.AtomicInteger类,或者在递增操作外部使用synchronized关键字。

    // 线程安全版本
    import java.util.concurrent.atomic.AtomicInteger;
    
    public abstract class Item {
        protected int id;
        // 使用AtomicInteger确保lastGeneratedId在多线程环境下的原子性递增
        protected static AtomicInteger lastGeneratedId = new AtomicInteger(0);
    
        protected Item() {
            // 获取当前值并递增,然后使用递增后的值
            this.id = 100000000 * getIdPrefix() + lastGeneratedId.incrementAndGet();
            System.out.println(toString());
        }
        // ... 其他代码不变
    }
  2. ID持久化: 如果系统需要重启,并且ID的递增序列需要从上次关闭时的状态继续,那么lastGeneratedId的值需要被持久化。这通常涉及将其存储到数据库、文件或缓存中,并在系统启动时加载其最新值。

  3. ID长度与类型: 当前的ID是9位数字,完全可以存储在int类型中。如果ID的位数更多,或者需要包含字母等非数字字符,则需要考虑使用long类型或String类型来存储ID。

  4. 起始值: lastGeneratedId的初始值设置为0,在构造函数中先递增再使用,因此第一个ID的后缀是1。如果要求第一个ID后缀是0,则需要调整lastGeneratedId的初始值或递增逻辑。例如,将lastGeneratedId初始化为-1,或在组合ID时使用lastGeneratedId.getAndIncrement()。

通过上述设计和实现,我们成功构建了一个灵活、可扩展且符合面向对象原则的ID生成系统,能够满足复杂规则下的唯一标识符生成需求。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

778

2023.08.22

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

183

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

287

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

258

2025.06.11

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号