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

我见过这样的例子:

public class MaxSeconds { public static final int MAX_SECONDS = 25; } 

并假设我可以有一个常量类包装常量,声明它们是静态的最后。 我几乎完全不知道Java,所以想知道这是否是创建常量的最佳方法。

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

 (public/private) static final TYPE NAME = VALUE; 

其中TYPE是类型, NAME是所有大写字母中带空格的下划线的名称, VALUE是常量值;

我强烈建议不要把你的常量放在自己的类或接口中。

作为一个方面说明:声明为final且变量可变的变量仍然可以改变; 但是,变量永远不能指向不同的对象。

例如:

 public static final Point ORIGIN = new Point(0,0); public static void main(String[] args){ ORIGIN.x = 3; } 

这是合法的, ORIGIN将是( ORIGIN一个点。

我强烈建议不要有一个常量类。 当时这似乎是一个好主意,但是当开发者拒绝记录常量,并且类增长到包含500个以上的所有不相关的常量(涉及应用程序的完全不同的方面)时,这一般变成常量文件是完全不可读的。 代替:

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

使用接口来保存常量(由Josh Bloch命名为常量接口模式 )是一个糟糕的做法。 以下是乔希建议的:

如果常量与现有的类或接口紧密相关,则应将其添加到类或接口中。 例如,所有盒装的数字基本类,如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; } 

关于命名约定:

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

在Effective Java(第二版)中,建议您使用枚举而不是静态整数作为常量。

这里有一个关于枚举的好书籍: http : //java.sun.com/j2se/1.5.0/docs/guide/language/enums.html

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

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

答案是:

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

只要避免使用接口:

 public interface MyConstants { String CONSTANT_ONE = "foo"; } public class NeddsConstant implements MyConstants { } 

这是诱人的,但违反封装和模糊类定义的区别。

我使用以下方法:

 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() {} } 

比如,我使用Constants.DB.Connection.URL来获取常量。 它看起来更像“东方对象”,就我而言。

在一个单独的类中创建静态的最终常量会让你陷入困境。 Java编译器实际上会优化它,并将常量的实际值放入引用它的任何类中。

如果以后更改了“常量”类,并且不会在其他引用该类的类上进行重新编译,则会结合使用旧值和新值。

不要把它们当作常量,把它们当作配置参数,并创建一个类来管理它们。 有价值是非最终的,甚至考虑使用获得者。 将来,如果您确定其中一些参数实际上应该由用户或管理员配置,那么将会更容易。

你可以做的第一个错误是创建一个全局可访问的类,用一个通用的名称来调用,比如Constants。 这只是乱七八糟的垃圾,你失去了所有的能力,找出你的系统使用这些常量的哪一部分。

相反,常数应该进入“拥有”它们的阶级。 你有一个常量叫TIMEOUT吗? 它可能应该进入你的Communications()或Connection()类。 MAX_BAD_LOGINS_PER_HOUR? 进入用户()。 等等等等。

另一个可能的用途是Java的.properties文件,当“常数”可以在运行时定义,但不容易用户更改。 你可以把它们打包在你的.jars文件中,并用class resourceLoader引用它们。

这是正确的路要走。

通常常量不会保存在单独的“常量”类中,因为它们不可被发现。 如果常数与当前类相关,那么保持它们可以帮助下一个开发者。

怎么枚举?

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

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

另一个好处是,为了修改常量,你不必重新编译所有使用它的东西。 当你引用一个静态final字段时,该常量的值被编译成引用它的任何字节码。

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

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

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

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

如果您使用的版本早于1.5,那么仍然可以使用普通的Java类来实现类型安全枚举。 (有关更多信息,请参见本网站 )。

基于上面的评论,我认为这是一个很好的方法来将老式的全局常量类(具有公共静态最终变量)改变为类似于枚举类似的方式:

 public class Constants { private Constants() { throw new AssertionError(); } public interface ConstantType {} public enum StringConstant implements ConstantType { DB_HOST("localhost"); // other String constants come here private String value; private StringConstant(String value) { this.value = value; } public String value() { return value; } } public enum IntConstant implements ConstantType { DB_PORT(3128), MAX_PAGE_SIZE(100); // other int constants come here private int value; private IntConstant(int value) { this.value = value; } public int value() { return value; } } public enum SimpleConstant implements ConstantType { STATE_INIT, STATE_START, STATE_END; } } 

那么我可以参考他们喜欢:

 Constants.StringConstant.DB_HOST 

一个好的面向对象设计不应该需要许多公共可用的常量。 大多数常量应该被封装在需要他们完成工作的类中。

有一定的意见来回答这个问题。 首先,java中的常量通常被声明为public,static和final。 原因如下:

 public, so that they are accessible from everywhere static, so that they can be accessed without any instance. Since they are constants it makes little sense to duplicate them for every object. final, since they should not be allowed to change 

我不会仅仅因为接口被实现而使用CONSTANTS访问器/对象的接口。 这看起来不好笑:

 String myConstant = IMyInterface.CONSTANTX; 

相反,我会根据一些小的权衡选择几种不同的方式,所以这取决于你需要什么:

 1. Use a regular enum with a default/private constructor. Most people would define constants this way, IMHO. - drawback: cannot effectively Javadoc each constant member - advantage: var members are implicitly public, static, and final - advantage: type-safe - provides "a limited constructor" in a special way that only takes args which match predefined 'public static final' keys, thus limiting what you can pass to the constructor 2. Use a altered enum WITHOUT a constructor, having all variables defined with prefixed 'public static final' . - looks funny just having a floating semi-colon in the code - advantage: you can JavaDoc each variable with an explanation - drawback: you still have to put explicit 'public static final' before each variable - drawback: not type-safe - no 'limited constructor' 3. Use a Class with a private constructor: - advantage: you can JavaDoc each variable with an explanation - drawback: you have to put explicit 'public static final' before each variable - you have the option of having a constructor to create an instance of the class if you want to provide additional functions related to your constants (or just keep the constructor private) - drawback: not type-safe 4. Using interface: - advantage: you can JavaDoc each variable with an explanation - advantage: var members are implicitly 'public static final' - you are able to define default interface methods if you want to provide additional functions related to your constants (only if you implement the interface) - drawback: not type-safe 

任何类型的常量都可以通过在类中创建一个不可变属性(即带有final修饰符的成员变量)来声明。 通常还会提供staticpublic修饰符。

 public class OfficePrinter { public static final String STATE = "Ready"; } 

有许多应用程序的常量值表示从n元组(例如枚举 )中进行选择。 在我们的例子中,我们可以选择定义一个枚举类型来限制可能的赋值(即改进的类型安全性 ):

 public class OfficePrinter { public enum PrinterState { Ready, PCLoadLetter, OutOfToner, Offline }; public static final PrinterState STATE = PrinterState.Ready; } 

一个单一的泛型常量类是一个坏主意。 常量应与它们逻辑上最相关的类组合在一起。

而不是使用任何类型的变量(尤其是枚举),我建议你使用方法。 创建一个与变量名称相同的方法,并让它返回分配给变量的值。 现在删除变量,并用对刚刚创建的方法的调用来替换它的所有引用。 如果你觉得这个常量是足够通用的,那么你不必为了使用它而创建一个类的实例,那么就把常量方法变成一个类方法。

FWIW,以秒为单位的超时值可能应该是一个配置设置(从属性文件读入或者像在Spring中一样通过注入),而不是常量。

有什么不同

1。

 public interface MyGlobalConstants { public static final int TIMEOUT_IN_SECS = 25; } 

2。

 public class MyGlobalConstants { private MyGlobalConstants () {} // Prevents instantiation public static final int TIMEOUT_IN_SECS = 25; } 

并使用MyGlobalConstants.TIMEOUT_IN_SECS无论我们需要这个常数。 我觉得两者都是一样的。

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

我们应该避免的一种方法是使用接口定义常量。

创建一个接口来声明常量实际上是最糟糕的事情:它打破了接口设计的原因:定义方法契约。

即使已经存在一个接口来解决特定的需求,但声明其中的常量实际上是没有意义的,因为常量不应该成为API的一部分,并且合同提供给客户端类。


为了简化,我们有广泛的4种有效方法

使用static final String/Integer字段:

  • 1)使用一个声明内部但不仅是常量的类。
  • 1变体)创建一个专用于只声明常量的类。

使用Java 5 enum

  • 2)在相关目的类(嵌套类)中声明枚举。
  • 2变种)单独创建枚举(不在外部类)。

TLDR:哪一个是最好的方法和找到常量?

在大多数情况下,枚举的方式可能比static final String/Integer方式更好 ,我个人认为,只有当我们有充分的理由不使用枚举时,才应该使用static final String/Integer方法。
关于我们应该在哪里声明常量值, 这个想法是搜索是否存在一个拥有强大的语义和使用关系的类与常量值。 如果我们找到它,我们应该用它作为常量的持有者 。 否则,常数应该与任何一个特定的类相关联。


static final String / static final Integerenum

枚举用法实际上是一种强烈考虑的方法。
枚举与StringInteger常量字段相比具有很大的优势。
他们设置了一个更强大的编译约束。 如果您定义了一个将枚举作为参数的方法,则只能传递枚举类(或null)中定义的枚举值。
使用String和Integer,你可以用任何兼容类型的值替换它们,即使在static final String / static final Integer字段中该值不是一个定义的常量,编译也可以。

例如,在一个类中定义了两个常量作为static final String字段:

 public class MyClass{ public static final String ONE_CONSTANT = "value"; public static final String ANOTHER_CONSTANT = "other value"; . . . } 

这里有一个方法,期望有一个这样的常量作为参数:

 public void process(String constantExpected){ ... } 

你可以这样调用它:

 process(MyClass.ONE_CONSTANT); 

要么

 process(MyClass.ANOTHER_CONSTANT); 

但是没有编译约束阻止你以这种方式调用它:

 process("a not defined constant value"); 

只有在运行时才会出现错误,只有在一次检查传输值时才会出错。

使用枚举,不需要检查,因为客户端只能在enum参数中传递枚举值。

例如,这里有两个在enum类中定义的值(开箱即用):

 public enum MyEnum { ONE_CONSTANT("value"), ANOTHER_CONSTANT(" another value"); private String value; MyEnum(String value) { this.value = value; } ... } 

这里有一个方法,期望有一个枚举值作为参数:

 public void process(MyEnum myEnum){ ... } 

你可以这样调用它:

 process(MyEnum.ONE_CONSTANT); 

要么

 process(MyEnum.ANOTHER_CONSTANT); 

但编译将永远不会允许您以这种方式调用它:

 process("a not defined constant value"); 

我们应该在哪里声明常量?

如果你的应用程序包含一个具有固定值的特定和强大的功能内聚的现有类,1)和2)显示更直观。
一般来说,如果这些常量是在主类中声明的,或者有一个非常自然的名字,那么我们可以在里面找到这个常量。

例如在JDK库中,指数和pi常数值是在声明不仅是常量声明( java.lang.Math )的类中声明的。

  public final class Math { ... public static final double E = 2.7182818284590452354; public static final double PI = 3.14159265358979323846; ... } 

使用数学函数的客户经常依赖Math类。 因此,他们可以很容易地找到常数,并且还可以记住EPI是以一种非常自然的方式定义的。

如果您的应用程序不包含具有非常特定和强大的功能内聚的恒定值的现有类,则1个变体和2个变体)方式显得更直观。
一般来说,如果这些常量是在一个操作它们的类中声明的,那么它并不会缓解这些常量的使用,而我们也有3或4个其他的类可以操纵它们,而且这些类中的任何一个看起来都不比其他类更自然。主机常量值。
在这里,定义一个只保持常量值的自定义类是有道理的。
例如,在JDK库中, java.util.concurrent.TimeUnit枚举不是在特定的类中声明的,因为并不是真正只有一个JDK特定的类,而是以最直观的方式显示它:

 public enum TimeUnit { NANOSECONDS { ..... }, MICROSECONDS { ..... }, MILLISECONDS { ..... }, SECONDS { ..... }, ..... } 

java.util.concurrent声明的很多类都使用它们: BlockingQueueArrayBlockingQueue<E>CompletableFutureExecutorService …实际上没有一个CompletableFuture持有枚举。

我不会像常量那样调用这个类(除了套管外)…我至少要有一类“设置”,“值”或“常数”,这些常量都可以存在。 如果我有大量的他们,我将他们在逻辑常量类(UserSettings,AppSettings等)

更进一步,可以将全局使用的常量放置在一个接口中,以便它们可以在系统范围内使用。 例如

 public interface MyGlobalConstants { public static final int TIMEOUT_IN_SECS = 25; } 

但是不要执行它。 只需通过完全限定的类名在代码中直接引用它们即可。

对于常量来说,枚举是一个更好的选择恕我直言。 这是一个例子

公共类myClass {

 public enum myEnum { Option1("String1", 2), Option2("String2", 2) ; String str; int i; myEnum(String str1, int i1) { this.str = str1 ; this.i1 = i } } 

我这样做的方法之一是通过创建一个具有常量值的“全局”类,并在需要访问该常量的类中进行静态导入。

static final is my preference, I'd only use an enum if the item was indeed enumerable.

I use static final to declare constants and go with the ALL_CAPS naming notation. I have seen quite a few real life instances where all constants are bunched together into an interface. A few posts have rightly called that a bad practice, primarily because that's not what an interface is for. An interface should enforce a contract and should not be a place to put unrelated constants in. Putting it together into a class that cannot be instantiated (through a private constructor) too is fine if the constant semantics don't belong to a specific class(es). I always put a constant in the class that it's most related to, because that makes sense and is also easily maintainable.

Enums are a good choice to represent a range of values, but if you are storing standalone constants with an emphasis on the absolute value (eg. TIMEOUT = 100 ms) you can just go for the static final approach.

I agree with what most are saying, it is best to use enums when dealing with a collection of constants. However, if you are programming in Android there is a better solution: IntDef Annotation .

 @Retention(SOURCE) @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST,NAVIGATION_MODE_TABS}) public @interface NavigationMode {} public static final int NAVIGATION_MODE_STANDARD = 0; public static final int NAVIGATION_MODE_LIST = 1; public static final int NAVIGATION_MODE_TABS = 2; ... public abstract void setNavigationMode(@NavigationMode int mode); @NavigationMode public abstract int getNavigationMode(); 

IntDef annotation is superior to enums in one simple way, it takes significantly less space as it is simply a compile-time marker. It is not a class, nor does it have the automatic string-conversion property.