我见过这样的例子:

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

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


当前回答

在单独的类中创建静态final常量可能会给您带来麻烦。Java编译器实际上会对此进行优化,并将常量的实际值放置到引用它的任何类中。

如果你稍后更改了Constants类,并且没有对引用该类的其他类进行严格的重新编译,那么你最终将使用新旧值的组合。

不要把它们看作常量,而应该把它们看作配置参数,并创建一个类来管理它们。让值是非final的,甚至可以考虑使用getter。将来,当您确定其中一些参数实际上应该由用户或管理员进行配置时,这将变得容易得多。

其他回答

这是一个坏习惯,非常可怕 引用约书亚·布洛赫的话,却不理解基本的原教旨主义。

我也没读过约书亚·布洛赫的书

他是个糟糕的程序员 或者迄今为止我发现引用他的人(约书亚是一个男孩的名字,我猜)只是把他的材料作为宗教脚本来证明他们的软件是宗教放纵的。

在圣经原教旨主义中,所有的圣经律法都可以总结为

全心全意地爱根本身份 爱邻居如爱自己

所以类似的软件工程原教旨主义可以总结为

全身心地投入到编程的基础知识中 像对待自己一样,为你的程序员同事的优秀而努力。

此外,在圣经原教旨主义的圈子中,一个强有力的合理的推论被得出

首先爱自己。因为如果你不爱自己,那么“像爱自己一样爱邻居”的概念就没有多大分量,因为“你有多爱自己”是你爱别人的基准线。

类似地,如果你不尊重自己作为一个程序员,只是接受一些编程大师的声明和预言,而不质疑基本原理,你对Joshua Bloch的引用和依赖是毫无意义的。因此,你实际上不尊重你的程序员同事。

软件编程的基本法则

懒惰是优秀程序员的美德 你要让你的编程生活尽可能简单,尽可能懒惰,因此尽可能有效 你要让你的编程结果和内脏尽可能简单,尽可能懒惰,因此对你的邻居程序员来说是尽可能有效的,他们和你一起工作,捡起你的编程内脏。

接口模式常量是个坏习惯??

在基本有效和负责任的编程法律下,这一宗教法令属于什么?

只要看看维基百科上关于接口模式常数的文章(https://en.wikipedia.org/wiki/Constant_interface),以及它针对接口模式常数的愚蠢借口。

Whatif-No IDE? Who on earth as a software programmer would not use an IDE? Most of us are programmers who prefer not to have to prove having macho aescetic survivalisticism thro avoiding the use of an IDE. Also - wait a second proponents of micro-functional programming as a means of not needing an IDE. Wait till you read my explanation on data-model normalization. Pollutes the namespace with variables not used within the current scope? It could be proponents of this opinion are not aware of, and the need for, data-model normalization Using interfaces for enforcing constants is an abuse of interfaces. Proponents of such have a bad habit of not seeing that "constants" must be treated as contract. And interfaces are used for enforcing or projecting compliance to a contract. It is difficult if not impossible to convert interfaces into implemented classes in the future. Hah .... hmmm ... ??? Why would you want to engage in such pattern of programming as your persistent livelihood? IOW, why devote yourself to such an AMBIVALENT and bad programming habit ?

无论借口是什么,当涉及到从根本上有效的软件工程时,都没有合理的借口来反对或通常不鼓励接口常量的使用。

制定美国宪法的国父们的初衷和精神状态如何并不重要。我们可以讨论开国元勋们的初衷,但我关心的是美国宪法的书面声明。每一个美国公民都有责任利用美国宪法中书面的文学原教旨主义,而不是不成文的立国意图。

Similarly, I do not care what the "original" intents of the founders of the Java platform and programming language had for the interface. What I care are the effective features the Java specification provides, and I intend to exploit those features to the fullest to help me fulfill the fundamental laws of responsible software programming. I don't care if I am perceived to "violate the intention for interfaces". I don't care what Gosling or someone Bloch says about the "proper way to use Java", unless what they say does not violate my need to EFFECTIVE fulfilling fundamentals.

基础是数据模型规范化

数据模型是如何托管或传输的并不重要。如果您不理解数据模型规范化的需求和过程,那么无论您使用接口、枚举还是其他什么,关系sql还是非sql。

我们必须首先定义并规范化一组流程的数据模型。当我们有一个一致的数据模型时,只有这样我们才能使用其组件的流程流来定义应用程序的领域或领域的功能行为和流程块。只有这样,我们才能定义每个功能流程的API。

甚至EF Codd提出的数据规范化方面现在也受到了严重的挑战。例如,他关于1NF的声明被批评为模棱两可、错位和过于简化,他的其他声明也是如此,特别是在现代数据服务、回购技术和传输的出现方面。在我看来,应该完全抛弃EF Codd语句,并设计一套新的数学上更合理的语句。

EF Codd的一个突出缺陷及其与有效的人类理解不一致的原因是,他相信人类可感知的多维、可变维度数据可以通过一组零碎的2维映射有效地感知。

数据规范化的基础

EF Codd未能表达的。

在每个连贯的数据模型中,这些是要实现的数据模型连贯的顺序递增顺序。

The Unity and Identity of data instances. design the granularity of each data component, whereby their granularity is at a level where each instance of a component can be uniquely identified and retrieved. absence of instance aliasing. i.e., no means exist whereby an identification produces more than one instance of a component. Absence of instance crosstalk. There does not exist the necessity to use one or more other instances of a component to contribute to identifying an instance of a component. The unity and identity of data components/dimensions. Presence of component de-aliasing. There must exist one definition whereby a component/dimension can be uniquely identified. Which is the primary definition of a component; where the primary definition will not result in exposing sub-dimensions or member-components that are not part of an intended component; Unique means of component dealiasing. There must exist one, and only one, such component de-aliasing definition for a component. There exists one, and only one, definition interface or contract to identify a parent component in a hierarchical relationship of components. Absence of component crosstalk. There does not exist the necessity to use a member of another component to contribute to the definitive identification of a component. In such a parent-child relationship, the identifying definition of a parent must not depend on part of the set of member components of a child. A member component of a parent's identity must be the complete child identity without resorting to referencing any or all of the children of a child. Preempt bi-modal or multi-modal appearances of a data-model. When there exists two candidate definitions of a component, it is an obvious sign that there exists two different data-models being mixed up as one. That means there is incoherence at the data-model level, or the field level. A field of applications must use one and only one data-model, coherently. Detect and identify component mutation. Unless you have performed statistical component analysis of huge data, you probably do not see, or see the need to treat, component mutation. A data-model may have its some of its components mutate cyclically or gradually. The mode may be member-rotation or transposition-rotation. Member-rotation mutation could be distinct swapping of child components between components. Or where completely new components would have to be defined. Transpositional mutation would manifest as a dimensional-member mutating into an attribute, vice versa. Each mutation cycle must be identified as a distinct data-modal. Versionize each mutation. Such that you can pull out a previous version of the data model, when perhaps the need arise to treat an 8 year old mutation of the data model.

在相互服务的组件应用程序的字段或网格中,必须有且只有一个一致的数据模型,或者存在数据模型/版本标识自身的方法。

我们还在问是否可以使用接口常量吗?真的吗?

有一些数据规范化问题比这个平凡的问题更重要。如果您不解决这些问题,那么您认为接口常量所引起的混乱相对来说是微不足道的。无价值之物。

根据数据模型归一化,然后将组件确定为变量、属性和契约接口常数。

然后确定哪些是值注入、属性配置占位符、接口、最终字符串等等。

如果您不得不以需要定位组件为借口,以便根据接口常量进行指示,这意味着您没有实践数据模型规范化的坏习惯。

也许你希望将数据模型编译成一个风险投资版本。您可以提取数据模型的明确可识别版本。

在接口中定义的值完全保证是不可变的。和共享。当你所需要的只是一组常量时,为什么要从另一个类加载一组最终字符串到你的类中呢?

那么为什么不发布一个数据模型契约呢?我的意思是,如果你能连贯地管理和规范它,为什么不呢?...

public interface CustomerService {
  public interface Label{
    char AssignmentCharacter = ':';
    public interface Address{
      String Street = "Street";
      String Unit= "Unit/Suite";
      String Municipal = "City";
      String County = "County";
      String Provincial = "State";
      String PostalCode = "Zip"
    }

    public interface Person {
      public interface NameParts{
        String Given = "First/Given name"
        String Auxiliary = "Middle initial"
        String Family = "Last name"
      }
    }
  }
}

现在我可以引用我的应用程序的合同标签的方式,如

CustomerService.Label.Address.Street
CustomerService.Label.Person.NameParts.Family

这会混淆jar文件的内容?作为一个Java程序员,我不关心jar的结构。

这给osgi驱动的运行时交换带来了复杂性?Osgi是一种非常有效的方法,可以让程序员继续他们的坏习惯。有比osgi更好的替代方案。

或者为什么不是这个?没有私人常数泄漏到公开的合同。所有私有常量都应该分组到一个名为“constants”的私有接口中,因为我不想不得不搜索常量,而且我懒得重复键入“私有最终字符串”。

public class PurchaseRequest {
  private interface Constants{
    String INTERESTINGName = "Interesting Name";
    String OFFICIALLanguage = "Official Language"
    int MAXNames = 9;
  }
}

甚至可能是这样:

public interface PurchaseOrderConstants {
  public interface Properties{
    default String InterestingName(){
       return something();
    }
    String OFFICIALLanguage = "Official Language"
    int MAXNames = 9;
  }
}

接口常量唯一值得考虑的问题是何时实现接口。

这不是接口的“初衷”吗?就像我关心国父们制定美国宪法的“初衷”,而不是最高法院如何解释美国宪法的书面条文??

毕竟,我生活在自由的土地上,荒野和勇敢者的家园。勇敢、自由、狂野——使用界面。如果我的程序员同事拒绝使用高效和懒惰的编程方式,我是否有义务按照黄金法则降低我的编程效率以与他们保持一致?也许我应该,但那不是理想的情况。

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

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

创建专门用于声明常量的接口实际上是最糟糕的事情:它违背了设计接口的初衷:定义方法契约。

即使已经存在一个接口来满足特定的需求,在其中声明常量也没有意义,因为常量不应该成为API和提供给客户端类的契约的一部分。


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

使用静态的最终字符串/整数字段:

1)使用在内部声明常量的类。 1变体)创建一个专门用于声明常量的类。

Java 5 enum:

2)在相关的目的类(嵌套类)中声明枚举。 2变体)创建枚举作为一个独立的类(在它自己的类文件中定义)。


TLDR:哪个是最好的方法,在哪里找到常数?

In most of cases, the enum way is probably finer than the static final String/Integer way and personally I think that the static final String/Integer way should be used only if we have good reasons to not use enums. And about where we should declare the constant values, the idea is to search whether there is a single existing class that owns a specific and strong functional cohesion with constant values. If we find such a class, we should use it as the constants holder. Otherwise, the constant should be associated to no one particular class.


静态final字符串/静态final整数vs enum

Enums usage is really a way to strongly considered. Enums have a great advantage over String or Integer constant field. They set a stronger compilation constraint. If you define a method that takes the enum as parameter, you can only pass a enum value defined in the enum class(or null). With String and Integer you can substitute them with any values of compatible type and the compilation will be fine even if the value is not a defined constant in the static final String/ static final Integer fields.

例如,下面两个在类中定义为静态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);

or

process(MyClass.ANOTHER_CONSTANT);

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

process("a not defined constant value");

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

对于enum,不需要检查,因为客户端只能在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);

or

process(MyEnum.ANOTHER_CONSTANT);

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

process("a not defined constant value");

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

如果您的应用程序包含一个现有的类,该类拥有特定的和强大的常量值函数内聚,则1)和2)看起来更直观。 一般来说,如果常量是在操纵它们的主类中声明的,或者有一个很自然的名字,我们可以在里面找到它,那么使用起来会更容易。

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

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

使用数学函数的客户端经常依赖于Math类。 因此,他们可以很容易地找到常数,也可以很自然地记住E和的定义。

If your application doesn't contain an existing class that has a very specific and strong functional cohesion with the constant values, the 1 variant) and the 2 variant) ways appear more intuitive. Generally, it doesn't ease the use of the constants if these are declared in one class that manipulates them while we have also 3 or 4 other classes that manipulate them as much as and no one of these classes seems be more natural than others to host constant values. Here, defining a custom class to hold only constant values makes sense. For example in the JDK library, the java.util.concurrent.TimeUnit enum is not declared in a specific class as there is not really one and only one JDK specific class that appear as the most intuitive to hold it :

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

在java.util.concurrent中声明的许多类都使用它们: BlockingQueue, ArrayBlockingQueue<E>, CompletableFuture, ExecutorService,…实际上,似乎没有谁比他们更适合主持这次全会。

我同意大多数人所说的,在处理常量集合时最好使用枚举。然而,如果你在Android上编程,有一个更好的解决方案: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注释在一个简单的方面优于枚举,它占用的空间明显更少,因为它只是一个编译时标记。它不是一个类,也没有自动字符串转换属性。

我不会称这个类与常量相同(除了大小写)…我将至少有一类“设置”,或“值”,或“常量”,所有的常量都将存在。如果我有很多,我会将它们分组在逻辑常量类中(UserSettings, AppSettings等)。

单一的泛型常量类不是一个好主意。常量应该按照它们在逻辑上最相关的类来分组。

我建议您使用方法,而不是使用任何类型的变量(特别是枚举)。创建一个与变量同名的方法,并让它返回分配给变量的值。现在删除该变量,并将对它的所有引用替换为对刚才创建的方法的调用。如果您觉得常量足够通用,不应该为了使用它而创建类的实例,那么可以将常量方法作为类方法。