一、怎样才算是常量
即需要具备怎样的特点,就可以称之为常量
- 由
final
关键字修饰
常量的特性
-
只能被赋值一次,赋值后值不再改变
-
对基本类型使用final关键字不能改变的是其数值
对于对象引用,不能改变的是其引用,而对象本身是可以修改的
关于静态常量
通过final关键字来声明常量,配合static关键字进行修饰,该常量则为静态常量,语法格式如下:
权限修饰符 static final 数据类型 常量名 = 常量值;
// 升序排序
public static final String ORDER_ASC = "ascending";
数据类型可以是基本数据类型,也可以是对象类型
二、定义常量的几种方式
接口常量
- 在interface中定义的常量默认是public static final类型
当接口用于实现或者继承的,如果我们在实现类或者子类接口中定义了同名的常量,那么引用实现类或者子类接口同名的常量,容易造成混乱
与接口的设计理念不相符,接口是一种规范,主要用来定义必须要实现的API接口,而不是暴露过多信息
源码参考:
com.baomidou.mybatisplus.core.toolkit.Constants com.alibaba.nacos.common.constant.HttpHeaderConsts
/**
* 描述 : 性别接口常量.
*
* @author : MoCha
* @version : v1
* @date : 2020-09-23 22:06
*/
public interface GenderConstants {
/**
* 男性
*/
int MALE = 1;
/**
* 女性
*/
int FEMALE = 2;
}
类常量
final修饰的类,不能被继承,但可以继承其他类,也可以实例化
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {}
如果常量类不想被继承或其他用途,可定义为public final class 类名 {},来保证不会被继承
私有化常量类构造方法,保证该类不会被外部实例化
源码参考:
java.nio.charset.StandardCharsets com.alibaba.csp.sentinel.slots.block.RuleConstant org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
/**
* 描述 : 权限类常量.
* <p>
* 说明:
* 1. final关键字修饰类,保证该类不会被继承
* 2. 私有化构造方法,保证该类不会被外部实例化
*
* @author : MoCha
* @version : v1
* @date : 2020-09-23 22:08
*/
public final class PermissionConstants {
/**
* 菜单权限
*/
public static final int MENU = 1;
/**
* 按钮权限
*/
public static final int BUTTON = 2;
/**
* 阻止实例化
* <p>
* Prevents instantiation
*/
private PermissionConstants() {
}
}
枚举常量
JLS(Java Language Specification,Java语言规范)提倡枚举项全部大写,字母之间用下划线分隔,这也是从常量的角度考虑的
枚举类型(enum type)是指由一组固定的常量组成合法的类型
枚举类不会被继承。因为JVM在生成枚举类时,将它声明为final
枚举类不允许去继承类。JVM在生成枚举类时已经继承了Enum类,由于Java是单继承,不支持再继承额外的类(唯一的继承位置被Enum类占用了)
枚举类的特性,既不能继承其他类,也不能被实例化,相当于是类常量的final修饰+私有实例化操作
Java 中提供了两个方便操作enum的工具类-EnumSet 和 EnumMap
源码参考:
com.baomidou.mybatisplus.core.enums.WrapperKeyword
/**
* 描述 : 结果状态枚举常量.
*
* @author : MoCha
* @version : v1
* @date : 2020-09-24 08:26
*/
@Getter
@AllArgsConstructor
public enum ResultStatusEnum {
/**
* 请求成功
*/
SUCCESS(200, "请求成功!"),
/**
* 服务器异常
*/
INTERNAL_SERVER_ERROR(500, "服务器睡着了!");
private final Integer code;
private final String message;
}
三、关于常量的几个深度思考
- 常量作为参数时,是String,int等弱类型,开发人员可以传入没有在常量中里定义的值,这个问题无法通过编译器发现
- 静态常量不支持字段扩展,每一个key对应一个值,而enum的每一个key可以拥有自己的字段
- 局部常量与静态常量的区别是?
- String为什么是不可变的,是因为final关键字的原因吗?
四、定义常量的最佳实践
-
一、不允许任何魔法值(即未经预先定义的常量)直接出现在代码中
-
二、在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟数字混淆,造成误解
例如:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
-
三、不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护
-
四、子工程内部共享常量:即在当前子工程的 constant 目录下
-
五、包内共享常量:即在当前包下单独的 constant 目录下
-
六、类内共享常量:直接在类内部 private static final 定义
-
七、强关联的常量推荐用枚举定义,其他类似于SUCCESS,或者YES等常量可用类常量来定义,不推荐用接口常量