【问题标题】:Static Import file or static import individual items静态导入文件或静态导入单个项目
【发布时间】:2018-03-06 12:09:49
【问题描述】:

我不确定我是对还是错,但 SonarLint 给了我无数关于重复使用某些字符串的警告。

因此,我为模块中的字符串创建了一个常量文件,该模块已在项目中的每个其他模块中访问。

我的想法是,如果我们不断被警告这一点。它可能会多次创建这些字符串中的每一个并暂时增加内存。 (它是一个 Web 应用程序,生成 JSON 和 XML,其中包含许多经常重复的术语,例如“身份”或“社区”)

我想知道的问题是,我的 IDE (IntelliJ) 似乎只是不断添加以下行:

import static com.*****.*****.resources.Constants.*

代替:

import static com.*****.*****.resources.Constants.PARAM_NAME_HASEMAIL;
import static com.*****.*****.resources.Constants.PARAM_NAME_HASSMS;
import static com.*****.*****.resources.Constants.PARAM_NAME_CMD;

请记住,该文件目前很小,大约有 100 个常量,但这个数字最终会达到 250 个。

首先我的问题是,哪个导入效率更高,只需导入文件,导入每个所需的常量,或者没关系(文件中最多肯定是 250 个常量)

我的第二个问题是,这值得付出努力吗(简单但繁重的工作)? 一个例子是:

data.has(PARAM_NAME_OPTIN)
data.remove(PARAM_NAME_OPTIN);
data.put(PARAM_NAME_OPTINTYPE, Coupon.OPTIN_MODE_SINGLE_OPTIN);

以上内容可能位于不同文件中的 3 或 4 个位置。 这两个常量的定义是:

public static final String PARAM_NAME_OPTIN             = "optin";
public static final String PARAM_NAME_OPTINTYPE         = "optInType";

下面是最严重的违规者。它是在从前端调用后端的每个方法中(在浏览器中的 ajax 请求之后):

json.put(PARAM_NAME_CMD, "Coupon.doSearchCouponEntriesByCoupon");
json.put(PARAM_NAME_APPID, PARAM_NAME_CAMPAIGN);
json.put(PARAM_NAME_COMMUNITYID, session.getAttribute(PARAM_NAME_COMMUNITYID));
json.put(PARAM_NAME_IDENTITYID, session.getAttribute(PARAM_NAME_IDENTITYID));

同样的定义是:

public static final String PARAM_NAME_APPID             = "applicationId";
public static final String PARAM_NAME_CMD               = "command";
public static final String PARAM_NAME_CAMPAIGN          = "*****campaign";
public static final String PARAM_NAME_COMMUNITYID       = "communityId";
public static final String PARAM_NAME_IDENTITYID        = "identityId";

我已为软件包名称加注星标以试图掩盖公司。即使这并没有真正共享任何 IP 或秘密,也比抱歉更安全。

感谢您提供的任何反馈(好的或坏的)。

附加信息:我正在为当前使用的每个文件手动导入的文件之一对这些常量有 22 个导入。 我想如果数字达到这样的高度,那么也许我应该改用*?还是它仍然对记忆有影响?

【问题讨论】:

    标签: java constants static-import


    【解决方案1】:

    我的想法是,如果我们不断被警告这一点。它可能会多次创建这些字符串中的每一个并暂时增加内存。 (它是一个 Web 应用程序,生成 JSON 和 XML,其中包含许多经常重复的术语,例如“identityId”或“communityId”)

    这实际上是错误的。在运行时,所有字符串文字都由类加载器进行实习。因此,如果您在许多不同的类中有 20 个 "identityId" 的示例,那么在运行时您将只有一个 String 对象代表文字的所有副本。 (这不是实现细节。JLS 保证这种行为。)

    SonarLint 警告的真正原因是同一字符串文字的多个副本可能会导致维护问题。如果您想将"identityId" 更改为"identityID",您有 20 个不同的地方可以更改它……而且 IDE 不会有太大帮助。

    首先我的问题是,哪个导入效率更高,只需导入文件,导入每个需要的常量,或者没那么重要

    它对运行时性能的影响为零,对编译速度的影响很可能微不足道。

    import 的不同风格最显着的影响是对源代码的可读性,这在很大程度上是一个见仁见智的问题。

    我的第二个问题是,这值得努力吗?

    绝对是一个意见问题......在你提供的例子中。

    但是,如果字符串是供用户阅读的消息,那么您可能需要将它们国际化。如果是这种情况,那么您最好将字符串存储在(例如)属性文件中......并根据用户的首选语言使用不同的文件。

    最后,假设您确实决定使用字符串常量(这是一个好主意),我不建议将它们全部放入一个大的“常量”类中。根据用途在普通类和接口中定义它们。

    【讨论】:

    • 感谢您提供的信息丰富的回答。你回答了我想知道的。这些值纯粹是为了我们的目的。客户的任何消息都已在属性文件中,并且具有适用于我们语言的正确 i18n 程序。我很感兴趣,编译器会实习所有的字符串,所以它对内存的使用没有帮助。然而,就像你说的,从维护 POV 来看,这是一个好主意,因为团队中的一些人相当懒惰,并且会复制粘贴方法、整个类等。所以任何确保这些术语都在一个地方的机会是有益!
    【解决方案2】:

    重复使用相同的文字字符串不会产生内存开销,因为 Java 源代码中的所有文字字符串都是interned。 SonarLint 警告您不是因为内存效率低下,而是因为存在错误风险和降低可读性。

    您提出的使用整个类的静态导入的解决方案的问题在于,当您稍后阅读使用它们的源代码时,您将不知道这些常量的来源。这就是为什么通常首选“静态导入”命名字段的原因。但是如果你有 250 个这样的常量,你可能不想在你的文件中添加 250 个静态导入行。

    不要将常量命名为PARAM_NAME_APPID 等,而是将它们放在一个名为ParamNames 的类中。然后你“静态导入”类名,这样你就可以看到它来自哪里,并且常量具有不言自明的名称:

    static import package.name.ParamNames;
    ....
    xxx = ParamNames.APP_ID;
    

    【讨论】:

    • 不错的主意。但是,由于将有 4 种类型的常量,因此主要的一种将是参数名称,并且占 250 个中的大部分(可能是 200 个,其他 3 个大约 15 个)
    【解决方案3】:

    Constants 类重构为属性文件。

    然后它包含像这样的常量

    PARAM_NAME_APPID=applicationId
    

    你可以使用它来加载它

    Properties constants = new Properties();
    try (FileReader reader = new FileReader("constants.properties")) {
        constants.load(reader);
    }
    

    【讨论】:

    • 很公平。可能值得做一次 readon init,多个 getter。但是....告诉我这如何回答我关于静态导入效率的问题,或者该任务是否真的值得(即它会减少内存占用)
    • 这不会妨碍优化吗?例如。如果一个常量是一个int,它可以被内联。如果它来自属性文件,则不能。
    • 另外一个属性文件似乎有点矫枉过正。这些是永远不会改变的价值观。关键是,每次创建这些项目时,它们都会向堆中添加更多字符串。如果有一个文件填充了这些字符串,它们只会创建一次,然后就可以使用。如果我们有 500 个用户在线,点击方法。它可能必须在一分钟内为每个用户创建 50 个字符串(在 StringBUilder 为 DHTMLX XML 树的完整结果或表单的 JSON 数据创建的字符串中)。使用常量文件,它只需要访问这些字符串。
    • @JamieReid 不确定你在说什么堆的东西,Properties 扩展了HashMap,所以你有每个String 的一个实例;这几乎是您在问题中建议的data 缓存,但没有样板代码。
    • 另一个答案清楚地说明了我的意思。另外,就像我说的那样,对于不变的值,属性将是矫枉过正的。我们有配置和语言元素的属性文件。这是严格的内部数据结构,最终网站将转换为可视形式或页面。