Java 中的常量——反模式

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

通常我们会在应用程序中看到常量文件的需求,它存储要在多个位置共享的常量。在设计应用程序时,我遇到了类似的情况,我们需要在应用程序的多个位置使用各种常量。

我确信,我需要一个单独的文件来存储公共静态常量。但我不太确定将其设为接口还是类。 (枚举不适合我的要求)。所以我有两个选择:

一个接口,例如


 package one;
public interface Constants {
String NAME="name1";
int MAX_VAL=25;
}

或者


 package one;
public interface Constants {
String NAME="name1";
int MAX_VAL=25;
}


我的观点是使用接口。我的论点是,由于接口自动将字段设置为 public static 和 final,这将是有利的,以防万一有人错过将它们添加到新常量。它还使代码看起来更简单、更清晰。

此外,简单测试表明相同的接口(字节类)的大小为 209 字节(在 ubuntu 14.04 上),而类(字节类)的大小为 366 字节(在相同的操作系统上)。更小的字节类意味着更少的加载和维护。此外,在加载接口时,JVM 无需担心它为类提供的额外功能(例如方法的重载/动态绑定等),因此加载速度更快。

这看起来不错,但这是反模式的典型案例。尽管使用接口作为常量可能看起来很有帮助,但它会留下一个漏洞,可能会影响以后的应用程序策略。

假设有一个类,它在很大程度上依赖于这些常量。开发人员在编写代码时看到接口名称在整个类中重复出现,例如


 package one;
public interface Constants {
String NAME="name1";
int MAX_VAL=25;
}


所以,为了 “清理” 他可能想要实现接口的代码,这样他就不需要到处写“packagename.Constants”,所有的常量都可以直接访问。

但是一旦他实现了接口,所有常量就成为他的“合同”的一部分(因为它们是公共的和静态的)。这可能会导致在类的契约中添加非必需的常量部分。这动摇了基本面,并增加了混乱。在 Java 中没有办法阻止接口被类实现。

另一方面,我们可以使类最终化,因此不可扩展。此外,我们可以将构造函数设为私有,因此此类是不可实例化的。由于新类是不可实例化的,因此它永远不会违约。此外,如果在同一个类中多次使用特定常量,开发人员可能会使用静态导入。

因此,一个好的 Constants 类设计应该是:


 package one;
public interface Constants {
String NAME="name1";
int MAX_VAL=25;
}

静态导入的一个例子是:


 package one;
public interface Constants {
String NAME="name1";
int MAX_VAL=25;
}


这样的设计问题也称为 Constant Interface Anti-pattern