常量概述
在Java中,常量(Constant) 是指在程序运行期间其值不能被改变的量。常量是程序中固定的、不可变的数据。
1. 常量的定义方式
1.1 字面量常量(Literal Constants)
直接在代码中写出的具体数值或字符串,也称为字面值。
// 整数常量
int age = 25;
// 浮点常量
double price = 99.99;
// 字符常量
char grade = 'A';
// 字符串常量
String name = "张三";
// 布尔常量
boolean isActive = true;
// null常量
String address = null;
1.2 final关键字定义的常量
使用final关键字修饰的变量,一旦赋值后就不能再修改。
// 基本数据类型的常量
final int MAX_COUNT = 100;
final double PI = 3.1415926;
final char NEW_LINE = '\n';
// 引用类型的常量(地址不可变,但对象内容可能可变)
final String COMPANY_NAME = "当贝科技";
final int[] NUMBERS = {1, 2, 3, 4, 5}; // 数组引用不可变,但数组元素可修改
1.3 编译期常量(Compile-time Constants)
满足以下条件的final变量被称为编译期常量:
- 使用
final修饰 - 数据类型为基本数据类型或String
- 在声明时就进行了初始化(使用常量表达式)
public class ConstantsExample {
// 编译期常量 - 在编译时就被替换为实际值
public static final int MAX_SIZE = 1000;
public static final String APP_NAME = "MyApplication";
// 这不是编译期常量(运行时初始化)
public static final double RANDOM_VALUE = Math.random();
}
2. 常量的命名规范
- 全大写字母:常量名通常使用全大写字母
- 下划线分隔:多个单词之间用下划线
_连接 - 语义清晰:名称要有明确的业务含义
// 推荐的命名方式
final int MAX_CONNECTIONS = 100;
final String DEFAULT_ENCODING = "UTF-8";
final double EARTH_RADIUS_KM = 6371.0;
// 不推荐的命名方式
final int mc = 100; // 缩写不清晰
final String de = "UTF-8"; // 缩写不清晰
final double er = 6371.0; // 缩写不清晰
3. 常量的作用域
3.1 局部常量
在方法内部定义的常量,作用域仅限于该方法。
public void calculateArea() {
final double PI = 3.1415926; // 局部常量
// ... 使用PI进行计算
}
3.2 成员常量
在类中定义的常量,可以是实例常量或静态常量。
public class Circle {
// 实例常量 - 每个对象都有自己的副本
private final double radius;
// 静态常量 - 所有对象共享,通常用于全局常量
public static final double PI = 3.1415926;
public Circle(double radius) {
this.radius = radius; // 实例常量必须在构造器中初始化
}
}
4. 常量的优势
4.1 提高代码可读性
使用有意义的常量名代替魔法数字,让代码更易理解。
// 不好的做法 - 魔法数字
if (user.getAge() >= 18) {
// ...
}
// 好的做法 - 使用常量
final int LEGAL_AGE = 18;
if (user.getAge() >= LEGAL_AGE) {
// ...
}
4.2 便于维护
当需要修改某个固定值时,只需要修改常量定义处即可。
// 如果税率从0.13改为0.15,只需修改一处
public static final double TAX_RATE = 0.13;
4.3 性能优化
编译期常量在编译时会被直接替换为字面量,避免了运行时的内存访问开销。
5. 常量池(Constant Pool)
Java中的字符串常量和基本数据类型的包装类常量会被存储在常量池中,以节省内存空间。
// 字符串常量池示例
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true - 指向同一个对象
// 基本数据类型包装类常量池(-128到127)
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2); // true - 缓存范围内的值
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i3 == i4); // false - 超出缓存范围
6. 注意事项
- final修饰引用类型:
final修饰引用类型变量时,变量的引用地址不能改变,但对象的内容可能仍然可以修改。
final List<String> names = new ArrayList<>();
names.add("张三"); // 允许 - 修改对象内容
names = new ArrayList<>(); // 编译错误 - 不能重新赋值引用
常量必须初始化:
final变量必须在声明时或构造器中进行初始化。接口中的常量:接口中定义的变量默认是
public static final的。
public interface DatabaseConfig {
String URL = "jdbc:mysql://localhost:3306/mydb"; // 等同于 public static final String URL
String USERNAME = "root";
String PASSWORD = "password";
}
7. 实际应用示例
public class AppConfig {
// 应用配置常量
public static final String APP_VERSION = "1.0.0";
public static final int SERVER_PORT = 8080;
public static final String DEFAULT_LANGUAGE = "zh-CN";
public static final long SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30分钟
// 数据库配置常量
public static final int MAX_POOL_SIZE = 20;
public static final int MIN_POOL_SIZE = 5;
public static final long CONNECTION_TIMEOUT_MS = 5000;
// 业务常量
public static final int MAX_RETRY_ATTEMPTS = 3;
public static final double DISCOUNT_THRESHOLD = 1000.0;
public static final String[] SUPPORTED_FORMATS = {"jpg", "png", "gif"};
}
通过合理使用常量,可以让Java程序更加健壮、可读和易于维护。