1. Overview
1.概述
The Java language specification does not define or even use the term compile-time constants. However, developers often use this term for describing a value that is not changed after compilation.
Java语言规范没有定义甚至没有使用编译时常量这个术语。然而,开发人员经常使用这个术语来描述一个在编译后不被改变的值。
In this tutorial, we’ll explore the differences between a class constant and a compile-time constant. We’ll look at constant expressions and see which data types and operators may be used for defining compile-time constants. Finally, we’ll look at a few examples where compile-time constants are commonly used.
在本教程中,我们将探讨类常量和编译时常量之间的区别。我们将研究常量表达式,看看哪些数据类型和运算符可用于定义编译时常量。最后,我们将看看几个经常使用编译时常量的例子。
2. Class Constants
2.类常量
When we use the term constant in Java, most of we the time, we are referring to static and final class variables. We cannot change the value of a class constant after compilation. Thus, all class constants of a primitive type or String are also compile-time constants:
当我们在Java中使用常量时,大多数时候,我们指的是静态和终极类变量。我们不能在编译后改变类常量的值。因此,所有原始类型或String的类常量也是编译时常量。
public static final int MAXIMUM_NUMBER_OF_USERS = 10;
public static final String DEFAULT_USERNAME = "unknown";
It’s possible to create constants that are not static. However, Java will allocate memory for that constant in every object of the class. Therefore, if the constant really has only one value, it should be declared static.
创建非静态的常量是可能的。但是,Java会在类的每个对象中为该常量分配内存。因此,如果常量真的只有一个值,它应该被声明为static。
Oracle has defined a naming convention for class constants. We name them uppercase with words separated by underscores. However, not all static and final variables are constants. If a state of an object can change, it is not a constant:
Oracle为类常量定义了一个命名规则。我们用大写字母来命名它们,用下划线隔开的单词。然而,并非所有的static和final变量都是常量。如果一个对象的状态可以改变,它就不是一个常量。
public static final Logger log = LoggerFactory.getLogger(ClassConstants.class);
public static final List<String> contributorGroups = Arrays.asList("contributor", "author");
Though these are constant references, they refer to mutable objects.
虽然这些是常量引用,但它们指的是可变的对象。
3. Constant Expressions
3.恒定表达式
The Java compiler is able to calculate expressions that contain constant variables and certain operators during code compilation:
Java编译器能够在代码编译期间计算包含常量变量和某些运算符的表达式。
public static final int MAXIMUM_NUMBER_OF_GUESTS = MAXIMUM_NUMBER_OF_USERS * 10;
public String errorMessage = ClassConstants.DEFAULT_USERNAME + " not allowed here.";
Expressions like these are called constant expressions, as the compiler will calculate them and produce a single compile-time constant. As defined in the Java language specification, the following operators and expressions may be used for constant expressions:
像这样的表达式被称为常量表达式,因为编译器将计算它们并产生一个单一的编译时常量。正如Java语言规范中所定义的那样,以下运算符和表达式可以用于常量表达式。
- Unary operators: +, -, ~, !
- Multiplicative operators: *, /, %
- Additive operators: +, –
- Shift operators: <<, >>, >>>
- Relational operators: <, <=, >, >=
- Equality operators: ==, !=
- Bitwise and logical operators: &, ^, |
- Conditional-and and the conditional-or operator: &&, ||
- Ternary conditional operator: ?:
- Parenthesized expressions whose contained expression is a constant expression
- Simple names that refer to constant variables
4. Compile vs. Runtime Constants
4.编译与运行时常量
A variable is a compile-time constant if its value is computed at compile-time. On the other hand, a runtime constant value will be computed during execution.
如果一个变量的值是在编译时计算的,那么它就是一个编译时常数。另一方面,运行时常量的值将在执行过程中被计算出来。
4.1. Compile-Time Constants
4.1.编译时常量
A Java variable is a compile-time constant if it’s of a primitive type or String, declared final, initialized within its declaration, and with a constant expression.
如果一个Java变量是原始类型或String,被声明为final,在其声明中被初始化,并且有一个常量表达式,那么它就是一个编译时常量。
Strings are a special case on top of the primitive types because they are immutable and live in a String pool. Therefore, all classes running in an application can share String values.
字符串是原始类型之上的一个特例,因为它们是不可变的,并且生活在字符串池中。因此,在一个应用程序中运行的所有类都可以共享String值。
The term compile-time constants include class constants, but also instance and local variables defined using constant expressions:
术语编译时常量包括类常量,但也包括使用常量表达式定义的实例变量和局部变量。
public final int maximumLoginAttempts = 5;
public static void main(String[] args) {
PrintWriter printWriter = System.console().writer();
printWriter.println(ClassConstants.DEFAULT_USERNAME);
CompileTimeVariables instance = new CompileTimeVariables();
printWriter.println(instance.maximumLoginAttempts);
final String username = "baeldung" + "-" + "user";
printWriter.println(username);
}
Only the first printed variable is a class constant. However, all three printed variables are compile-time constants.
只有第一个打印出来的变量是一个类常量。然而,所有三个打印的变量都是编译时常量。
4.2. Run-Time Constants
4.2.运行时常量
A runtime constant value cannot change while the program is running. However, each time when we run the application, it can have a different value:
一个运行时常量的值在程序运行时不能改变。然而,每次当我们运行程序时,它可以有一个不同的值。
public static void main(String[] args) {
Console console = System.console();
final String input = console.readLine();
console.writer().println(input);
final double random = Math.random();
console.writer().println("Number: " + random);
}
Two run-time constants are printed in our example, a user-defined value and a randomly generated value.
在我们的例子中打印了两个运行时常量,一个是用户定义的值,一个是随机生成的值。
5. Static Code Optimization
5.静态代码优化
The Java compiler statically optimizes all compile-time constants during the compilation process. Therefore, the compiler replaces all compile-time constant references with their actual values. The compiler performs this optimization for any classes where compile-time constants are used.
Java 编译器在编译过程中静态地优化所有编译时常量。因此,编译器将所有编译时常量引用替换为其实际值。编译器对任何使用了编译时常量的类都进行了这种优化。
Let’s take a look at an example where a constant from another class is referenced:
让我们来看看一个例子,其中引用了另一个类中的常量。
PrintWriter printWriter = System.console().writer();
printWriter.write(ClassConstants.DEFAULT_USERNAME);
Next, we’ll compile the class and observe the generated bytecode for the above two lines for code:
接下来,我们将编译该类,并观察上述两行代码的生成字节码。
LINENUMBER 11 L1
ALOAD 1
LDC "unknown"
INVOKEVIRTUAL java/io/PrintWriter.write (Ljava/lang/String;)V
Note that the compiler replaced the variable reference with its actual value. Consequently, in order to change a compile-time constant, we need to recompile all classes which are using it. Otherwise, the old value would continue to be used.
请注意,编译器将变量引用替换成了它的实际值。因此,为了改变一个编译时常量,我们需要重新编译所有正在使用它的类。否则,旧的值将继续被使用。
6. Use Cases
6.使用案例
Let’s take a look at two common use cases for compile-time constants in Java.
让我们来看看Java中编译时常量的两个常见用例。
6.1. Switch Statement
6.1.开关声明
When defining the cases for a switch statement, we need to adhere to the rules defined in the Java language specification:
在定义switch语句的情况时,我们需要遵守Java语言规范中定义的规则。
- The case labels of the switch statement require values that are either constant expressions or enum constants
- No two of the case constant expressions associated with a switch statement may have the same value
The reason behind this is that the compiler compiles switch statements into bytecode tableswitch or lookupswitch. They require the values used in the case statement to be both compile-time constants and unique:
这背后的原因是,编译器将switch语句编译成字节码tableswitch或lookupswitch.,它们要求case语句中使用的值既是编译时常量又是唯一的。
private static final String VALUE_ONE = "value-one"
public static void main(String[] args) {
final String valueTwo = "value" + "-" + "two";
switch (args[0]) {
case VALUE_ONE:
break;
case valueTwo:
break;
}
}
The compiler will throw an error if we do not use constant values in our switch statement. However, it will accept a final String or any other compile-time constant.
如果我们不在switch语句中使用常量值,编译器会抛出一个错误。然而,它将接受一个final String或任何其他编译时常量。
6.2. Annotations
6.2.注释
Annotation processing in Java takes place at compile time. In effect, that means that annotation parameters can only be defined using compile-time constants:
Java中的注释处理是在编译时进行的。实际上,这意味着注释参数只能使用编译时常量来定义。
private final String deprecatedDate = "20-02-14";
private final String deprecatedTime = "22:00";
@Deprecated(since = deprecatedDate + " " + deprecatedTime)
public void deprecatedMethod() {}
Though it’s more common to use class constants in this situation, the compiler allows this implements, as it recognizes the values as immutable constants.
虽然在这种情况下使用类常量更常见,但编译器允许这种实现,因为它将这些值识别为不可变的常量。
7. Conclusion
7.结语
In this article, we explored the term compile-time constants in Java. We saw that the term includes class, instance, and local variables of a primitive type or String, declared final, initialized within its declaration, and defined with a constant expression.
在这篇文章中,我们探讨了Java中的编译时常量这一术语。我们看到,该术语包括原始类型或String的类、实例和局部变量,声明为final,在其声明中初始化,并以常量表达式定义。
In the examples, we saw the difference between compile-time and run-time constants. We also saw that the compiler uses compile-time constants to perform static code optimization.
在这些例子中,我们看到了编译时和运行时常量之间的区别。我们还看到,编译器使用编译时常量来执行静态代码优化。
Finally, we looked at the usage of compile-time constants in switch statements and Java annotations.
最后,我们研究了开关语句和Java注解中编译时常量的用法。
As always, the source code is available over on GitHub.
一如既往,源代码可在GitHub上获取。