在Java中实现常量的最佳方法是什么?

mk.*_*mk. 373 java constants

我见过这样的例子:

public class MaxSeconds {
   public static final int MAX_SECONDS = 25;
}
Run Code Online (Sandbox Code Playgroud)

并且假设我可以有一个Constants类来包装常量,声明它们是静态final.我几乎不知道Java,我想知道这是否是创建常量的最佳方法.

jjn*_*guy 403

这是完全可以接受的,甚至可能是标准.

(public/private) static final TYPE NAME = VALUE;
Run Code Online (Sandbox Code Playgroud)

TYPEtype 在哪里,NAME是带有下划线的所有大写字母的名称,VALUE是常量值;

我强烈建议不要将常量放在自己的类或接口中.

作为旁注:声明为final且可变的变量仍然可以更改; 但是,变量永远不能指向不同的对象.

例如:

public static final Point ORIGIN = new Point(0,0);

public static void main(String[] args){

    ORIGIN.x = 3;

}
Run Code Online (Sandbox Code Playgroud)

这是合法的,ORIGIN然后是(3,0)点.

  • 以前关于import static的评论仅适用于Java 5+.有些人认为速记不值得混淆,因为常量来自何处,当读取长代码时,MaxSeconds.MAX_SECONDS可能更容易跟随然后上升并查看导入. (23认同)
  • 你甚至可以'导入静态MaxSeconds.MAX_SECONDS;' 这样你就不必拼写MaxSeconds.MAX_SECONDS (19认同)
  • 如果你正在阅读这个问题,请仔细阅读下面的两个答案,然后再认真对待这个答案,尽管如果没有接受它会引起争议. (14认同)
  • 因为你可以像jjnguy所展示的那样改变objetc,最好是你的constatns是不可变对象或只是普通的原始/字符串. (5认同)
  • @jjnguy你没有错,但如果我只阅读你的答案的问题和前几行,我会认为"一个常数类"是"完全可以接受的,甚至可能是标准".*那个*概念*是错误的. (3认同)

Met*_*002 235

我强烈建议不要有一个常量类.当时看起来似乎是一个好主意,但是当开发人员拒绝记录常量并且类增长到包含超过500个常量时,这些常量根本不相关(与应用程序的完全不同的方面有关),通常会变成完全不可读的常量文件.代替:

  • 如果您可以访问Java 5+,请使用枚举来定义应用程序区域的特定常量.对于这些常量,应用程序区域的所有部分都应引用枚举,而不是常量值.您可以声明类似于声明类的枚举.枚举可能是Java 5+中最有用(也可以说是唯一)的有用功能.
  • 如果您拥有仅对特定类或其子类之一有效的常量,请将它们声明为protected或public,并将它们放在层次结构中的顶级类中.这样,子类可以访问这些常量值(如果其他类通过public访问它们,则常量不仅对特定类有效...这意味着使用此常量的外部类可能与包含常量的类)
  • 如果您有一个定义了行为的接口,但返回的值或参数值应该是特定的,那么在该接口上定义常量是完全可以接受的,这样其他实现者就可以访问它们.但是,避免创建一个仅用于保存常量的接口:它可能变得像为保持常量而创建的类一样糟糕.

  • 关闭主题,但是......泛型当然不是很完美,但我认为你可以为它们提供一个非常好的案例,因为它们是Java 5的有用功能:) (30认同)
  • 完全同意......这可以记录为大型项目的经典反模式. (12认同)
  • @ŁukaszL.这个问题本身就是基于意见的(每当"最佳"出现时,通常都是意见问题),所以答案是这个问题的有效答案.我已经给出了答案(是的,我*相信*是,所以是的,这是一种意见,因为再次"最佳"随时间变化并且通常基于意见)在Java中实现常量的最佳方法是. (3认同)

Mar*_*iar 120

使用接口只是为了保持常量(由Josh Bloch 命名为常量接口模式)是一个不好的做法.这是Josh建议的:

如果常量与现有类或接口紧密相关,则应将它们添加到类或接口.例如,所有盒装数字基本类(如Integer和Double)都会导出MIN_VALUE和MAX_VALUE常量.如果常量最好被视为枚举类型的成员,则应使用枚举 类型导出它们.否则,您应该使用不可实例化的实用程序类导出常量.

例:

// Constant utility class
package com.effectivejava.science;
public class PhysicalConstants {
    private PhysicalConstants() { }  // Prevents instantiation

    public static final double AVOGADROS_NUMBER   = 6.02214199e23;
    public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
    public static final double ELECTRON_MASS      = 9.10938188e-31;
}
Run Code Online (Sandbox Code Playgroud)

关于命名约定:

按照惯例,这些字段的名称由大写字母组成,单词由下划线分隔.这些字段包含原始值或对不可变对象的引用至关重要.

  • 如果你打算做一些不好的练习,或许你应该解释为什么你认为它是? (23认同)
  • 这是一个老帖子,但最好使用`abstract`表示法而不是私有构造函数. (14认同)
  • @XtremeBiker标记类`abstract`而不是私有构造函数并不能完全阻止实例化,因为可以将它子类化并实例化子类(不是这样做是个好主意,但它是可能的).@ToolmakerSteve你不能使用私有构造函数对类进行子类化(至少不会没有严重的错误),因为子类的构造函数需要调用其超类的(现在是私有的)构造函数.因此,将其标记为"final"是不必要的(但可能更明确). (7认同)
  • 马特,虽然骑车人的建议不是假的,但我会主张最后的课程,而不是抽象的课程.骑车者的观点是,你要确保你的常数类不可改变.因此,通过将其标记为final,您不允许对其进行子类化或实例化.这也有助于封装其静态功能,并且不允许其他开发人员对其进行子类化并使其执行不设计的操作. (5认同)
  • 使用类只是为了保持常量更糟糕.如果您从不想创建该类的对象,为什么首先使用常规**类**? (3认同)
  • 要强制执行不可实例化的类,最好在私有构造函数中抛出新的AssertionError();`以确保它不通过反射实例化. (2认同)

小智 36

在Effective Java(第2版)中,建议您使用枚举而不是静态整数来表示常量.

这里有关于Java的枚举的好文章:http: //java.sun.com/j2se/1.5.0/docs/guide/language/enums.html

请注意,在该文章的最后提出的问题是:

那么什么时候应该使用枚举?

答案是:

任何时候你需要一组固定的常量


小智 21

只是避免使用界面:

public interface MyConstants {
    String CONSTANT_ONE = "foo";
}

public class NeddsConstant implements MyConstants {

}
Run Code Online (Sandbox Code Playgroud)

这很诱人,但是违反了封装并模糊了类定义的区别.


alb*_*.ua 19

我使用以下方法:

public final class Constants {
  public final class File {
    public static final int MIN_ROWS = 1;
    public static final int MAX_ROWS = 1000;

    private File() {}
  }

  public final class DB {
    public static final String name = "oups";

    public final class Connection {
      public static final String URL = "jdbc:tra-ta-ta";
      public static final String USER = "testUser";
      public static final String PASSWORD = "testPassword";

      private Connection() {}
    }

    private DB() {}
  }

  private Constants() {}
}
Run Code Online (Sandbox Code Playgroud)

比如,我Constants.DB.Connection.URL用来保持不变.对我来说,它看起来更"面向对象".

  • 有趣,但繁琐.为什么不按照其他人的建议在与他们关系最密切的类中创建常量呢?例如,在其他地方的数据库代码中,您是否有连接的基类?例如"ConnectionBase".然后你可以把常量放在那里.任何使用连接的代码都可能已经有一个导入,只能说"ConnectionBase.URL"而不是"Constants.DB.Connection.URL". (4认同)
  • @ToolmakerSteve但是多个类可以使用的常规常量呢?例如,样式,Web服务URL等...? (3认同)

Kev*_*Day 17

在单独的类中创建静态最终常量可能会让您遇到麻烦.Java编译器实际上会对此进行优化,并将常量的实际值放入任何引用它的类中.

如果您稍后更改了"常量"类,并且您没有对引用该类的其他类进行硬重新编译,那么最终将使用旧值和新值的组合.

不要将它们视为常量,而应将它们视为配置参数并创建一个类来管理它们.这些值是非最终的,甚至考虑使用getter.将来,当您确定某些参数实际上应该由用户或管理员配置时,它将更容易实现.


Yan*_*min 13

您可以犯的第一个错误是创建一个全局可访问的类,使用通用名称调用,如常量.这简直就是乱七八糟的垃圾,你失去了弄清楚系统中哪些部分使用这些常量的能力.

相反,常量应该进入"拥有"它们的类.你有一个名为TIMEOUT的常量吗?它可能应该进入您的Communications()或Connection()类.MAX_BAD_LOGINS_PER_HOUR?进入User().等等等等.

另一种可能的用途是Java .properties文件,当"常量"可以在运行时定义,但不容易用户更改.您可以将它们打包在.jars中,并使用Class resourceLoader引用它们.


Jas*_*hen 6

这是正确的方法.

通常,常量不会保存在单独的"常量"类中,因为它们不可发现.如果常量与当前类相关,那么将它们保留在那里有助于下一个开发人员.


Séb*_* D. 5

枚举怎么样?


小智 5

我同意使用界面不是要走的路.在Bloch的Effective Java中,避免这种模式甚至有自己的项目(#18).

Bloch对常量接口模式的一个参数是使用常量是一个实现细节,但实现一个接口来使用它们会在导出的API中公开该实现细节.

public|private static final TYPE NAME = VALUE;模式是声明常量的好方法.就个人而言,我认为最好避免单独设置一个容纳你所有常数的课程,但除了个人偏好和风格外,我从未见过不这样做的理由.

如果您的常量可以作为枚举进行良好建模,请考虑1.5或更高版本中可用的枚举结构.

如果您使用的是1.5之前的版本,则仍然可以使用普通的Java类来提取类型安全枚举.(有关详细信息,请参阅此网站).


小智 5

我更喜欢使用getter而不是常量.那些getter可能会返回常量值,例如public int getMaxConnections() {return 10;},但是任何需要常量的东西都会经过一个getter.

一个好处是,如果你的程序超出常量 - 你发现它需要是可配置的 - 你可以改变getter返回常量的方式.

另一个好处是,为了修改常量,您不必重新编译使用它的所有内容.当您引用静态final字段时,该常量的值将编译为引用它的任何字节码.

  • 好的重新编译引用类在21世纪几乎不是负担.并且你永远不应该使用accessor/mutator(getter/setter)模型来处理*其他*而不是访问和变异成员变量.常量在概念上意味着在本质上是立即的,而吸气剂/制定者(两者)意味着管理**状态**.此外,你只是要求混淆:人们不会指望一个吸气剂只能产生恒定. (5认同)