版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷

虽然推崇在java中使用枚举(可查看《Java中的枚举的治理》)来对数据字典及常量进行控制,但是有些时候,我们还是会觉得常量控制更为便捷。

比如,对于数据字典,我们可以使用枚举值来处理;对于一些其他的信息,我们会使用常量保存和使用。

一、常量遇到的问题

1.苗条的常量类

这里使用苗条形容下我们程序中的常量类,别看它宽度,就只看她长度,滚起屏来,那叫一个长啊,修长的身材,令你如痴如醉。(省略号里的东西,我就不贴了!!!)

例如:

public class Constants {

        public static final String REAL_NAME1 = "v1";
public static final String REAL_NAME2 = "v2";
public static final String REAL_NAME3 = "v3";
public static final String REAL_NAME4 = "v4";
public static final String REAL_NAME5 = "v5";
public static final String 6 = "v6";
public static final String 7 = "v7";
public static final String REAL_NAME8 = "v8";
public static final String REAL_NAME9 = "v9";
......
}

   一个无穷尽的常量类,设想这个类篇幅巨长,你想加点什么不知道该加在哪;你想改点什么,不知道去哪改;你想骂街也不知道去哪骂!!!

这样的常量管理,带来的问题如下:

  a.不好维护,相关的代码写到一起,此时,常量的篇幅较长导致你找不到对应的常量块进行维护;

  b.虽然是在不同的业务场景下,但是有些常量的名称还是有可能重复;

  c.有时为了减少常量的定义,就得共用一些常量,而这样的共用会导致某种业务场景下需要对该常量进行修改,而导致另外一些业务场景下的常量使用产生歧义;

  d.其他你能想到的骂街的理由

2.代码的坏味道

引用下“代码的坏味道”这个词,我们常能看到一些常量类的坏味道里,假如常量名称如上所示,名称类似的很多;名称不明确的也很多,还没有注释,这样的歧义也是因为代码不好管理造成的。

二、常量如何治理

1.初级治理——使用内部类

  使用java的内部类进行常量的初步治理,代码如下:

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
public class Constants { public static final class TOKEN_FLAG_ONE { public static final String REAL_NAME = "v1"; public static final String CRET = "v2"; public static final String GUR = "v5"; } }

  这样的好处是,通过常量的内部类的名称,可以直接获取对应模块的常量的引用信息。使用代码如下:

    @Test
public void test(){
System.out.println(Constants.TOKEN_FLAG_ONE.REAL_NAME);
}

2.中极治理——集中管理

  初级治理中,我们的想法还是不错的,但是看起来比较low,而且当我们希望通过value获取到key时,你却无能为力,于是我们有了中级治理。

  中级治理我们主要是通过map,每个内部类都会存为map中的一个entry,每个entry又都是map类型的集合,集合中包含该内部类的所有常量。

  代码如下:

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
public class Constants { public static final Map<String,Map<String,String>> keyValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueKeyMapCons = new LinkedHashMap<String, Map<String, String>>();
  /**
* 初始化所有常量
*/
static {
try {
//获取所有内部类
for (Class cls : Constants.class.getClasses()) {
Map<String, String> keyValueMap = new LinkedHashMap<String, String>();//存放key和value的map
Map<String, String> valueKeyMap = new LinkedHashMap<String, String>();//存放value和key的map//每个内部类-获取所有属性(不包括父类的)
for (Field fd : cls.getDeclaredFields()) {
keyValueMap.put(fd.getName(), fd.get(cls).toString());//注解对象空,其值为该field的值
valueKeyMap.put(fd.get(cls).toString(),fd.getName());
}
keyValueMapCons.put(cls.getSimpleName(),keyValueMap);
valueKeyMapCons.put(cls.getSimpleName(),valueKeyMap);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static final class TOKEN_FLAG_ONE {
public static final String REAL_NAME = "v1";
public static final String CRET = "v2"; public static final String GUR = "v5"; }
}

  好了,我们在Constants中有了两个常量集:一个集keyValueMapCons,按照内部类的名称,把常量的名字作为map的key,值作为map的value;一个集valueKeyMapCons,按照内部类的名称,把常量的值作为map的key,常量的名字作为map的key。

  这样一来,我们可以使用常量的这两个集满足我们对常量的使用需求。使用代码如下:

    @Test
public void test1(){
System.out.println(Constants.keyValueMapCons.get("TOKEN_FLAG_ONE").get("REAL_NAME"));//v1
System.out.println(Constants.valueKeyMapCons.get("TOKEN_FLAG_ONE").get("v5"));//GUR
}

3.中高级治理——使用注解

  我们可以通过key获取到value,也可以通过value获取到key了。现在有这么个问题,我们使用常量时,不光要有常量的定义、常量的值,还应该有对常量的描述,而传统的对于常量的定义,往往使我们无从存放对常量的描述。

  此时,我们希望通过注解来改变这种情况。

  我们来自定义注解类型如下:

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface ConstantAnnotation {
String value();
}

  有了这个注解,我们就可以把描述些到常量的上面了

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
public class Constants {
private static final String CONSTANT_STRING = "这是一条短信消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("实名")
public static final String REAL_NAME = "v1"; @ConstantAnnotation("证书")
public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING)
public static final String GUR = "v5";//把描述与注解分开 }
}

  我们看到,每个常量上面有了对应的中文描述,这样的中文描述可以用来干嘛呢?

  比如,我们希望在某个业务场景下,符合gur常量的业务,发送一条短信消息,而这个消息我们就可以定义在我们的自定义注解中。例如GUR这个常量,我们把它的描述声明成一个常量,这个常量可用来存放对应的短信消息。我们的常量类中如果再有一个通过常量获取到描述的map,这是不是就完美了?

  于是,我们有了下面的代码:

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
public class Constants { public static final Map<String,Map<String,String>> keyDescMapCons = new LinkedHashMap<String, Map<String, String>>();
/**
* 初始化所有常量
*/
static {
try {
//获取所有内部类
for (Class cls : Constants.class.getClasses()) {
Map<String, String> keyDescMap = new LinkedHashMap<String, String>();//存放key和desc的map //每个内部类-获取所有属性(不包括父类的)
for (Field fd : cls.getDeclaredFields()) {
//每个属性获取指定的annotation的注解对象
ConstantAnnotation ca = fd.getAnnotation(ConstantAnnotation.class);
if(ca != null){
keyDescMap.put(fd.getName(), ca.value());//注解对象不空,其值为注解对象中的值
}
}
keyDescMapCons.put(cls.getSimpleName(),keyDescMap); }
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* key-value-desc
*
* 常量名-常量值-注解描述
*
* 实现通过key获取value
*
* 实现通过key获取desc
*
* 实现通过value获取desc
*/
private static final String CONSTANT_STRING = "这是一条短消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("实名")
public static final String REAL_NAME = "v1"; @ConstantAnnotation("证书")
public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING)
public static final String GUR = "v5";//把描述与注解分开 } }

  现在,我们通过Constants的keyDescMap可以获取到这个常量对应的描述了。使用代码如下:

    @Test
public void test1(){
System.out.println(Constants.keyDescMapCons.get("TOKEN_FLAG_ONE").get("GUR"));//打印输出“这是一条短消息”
}

4.综合治理(终极治理)

  现在我们有了常量的治理,有了注解的描述,有时我们需要通过key获取到value,有时我们需要通过value获取描述,有时我们需要通过key获取到描述,等等。排列组合共6种形式,暂且不说什么时候会用到,我们先给他来个综合治理的实现。代码如下:

/**
* Created by SZBright on 2017/3/1.
*
* @author :
*/
public class Constants { public static final Map<String,Map<String,String>> keyValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> keyDescMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> descValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> descKeyMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueDescMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueKeyMapCons = new LinkedHashMap<String, Map<String, String>>(); /**
* 初始化所有常量
*/
static {
try {
//获取所有内部类
for (Class cls : Constants.class.getClasses()) {
Map<String, String> keyDescMap = new LinkedHashMap<String, String>();//存放key和desc的map
Map<String, String> keyValueMap = new LinkedHashMap<String, String>();//存放key和value的map
Map<String, String> valueKeyMap = new LinkedHashMap<String, String>();//存放value和key的map
Map<String, String> valueDescMap = new LinkedHashMap<String, String>();//存放value和desc的map
Map<String, String> descValueMap = new LinkedHashMap<String, String>();//存放desc和value的map
Map<String, String> descKeyMap = new LinkedHashMap<String, String>();//存放desc和key的map
//每个内部类-获取所有属性(不包括父类的)
for (Field fd : cls.getDeclaredFields()) {
//每个属性获取指定的annotation的注解对象
ConstantAnnotation ca = fd.getAnnotation(ConstantAnnotation.class);
keyValueMap.put(fd.getName(), fd.get(cls).toString());//注解对象空,其值为该field的值
valueKeyMap.put(fd.get(cls).toString(),fd.getName());
if(ca != null){
keyDescMap.put(fd.getName(), ca.value());//注解对象不空,其值为注解对象中的值
valueDescMap.put(fd.get(cls).toString(),ca.value());
descValueMap.put(ca.value(),fd.get(cls).toString());
descKeyMap.put(ca.value(),fd.getName());
}
}
keyValueMapCons.put(cls.getSimpleName(),keyValueMap);
keyDescMapCons.put(cls.getSimpleName(),keyDescMap);
descValueMapCons.put(cls.getSimpleName(),descValueMap);
descKeyMapCons.put(cls.getSimpleName(),descKeyMap);
valueDescMapCons.put(cls.getSimpleName(),valueDescMap);
valueKeyMapCons.put(cls.getSimpleName(),valueKeyMap);
}
} catch (Exception e) {
e.printStackTrace();
}
} private static final String CONSTANT_STRING = "这是一条短消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("实名")
public static final String REAL_NAME = "v1"; @ConstantAnnotation("证书")
public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING)
public static final String GUR = "v5";//把描述与注解分开 }
}

  好了,有了上面这个类,你就啥都不用愁了,用的时候先拿常量的声明,再用对应的map,然后指定内部类名称,想获取什么,获取什么。

  打完收工。

  ps:枚举治理和常量治理结合使用,可能是我们系统开发时的最佳实践,欢迎有更高效方法的朋友多多指教。

-------------------------------------------里程碑----------------------------------------------

  至此,通过反射、自定义注解来解决系统中一些常见场景的使用问题告一段落,相关文章可参见:

   敏感信息加密处理

   Java中的枚举治理

-------------------------------------------里程碑----------------------------------------------

Java中的常量治理的更多相关文章

  1. 【Java_基础】java中的常量池

    1.java常量池的介绍 java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池. java常量池简介:java常量池中保存了一份在 ...

  2. Java入门以及Java中的常量与变量总结

    JDK与JRE的区别: JDK给开发人员使用(包含开发工具),JRE给客户使用(运行java程序的核心类库),JDK包含JRE关键字的含义: JAVA语言赋予特殊含义,具有专门用途的单词,关键字的单词 ...

  3. JAVA中的常量定义在class中还是interface中比较合理?

    本文地址:http://blog.csdn.net/sushengmiyan 本文作者:苏生米沿 java中使用的常量可以集中定义在一个文件中. 有两种解决方案: 1.在Constants.java中 ...

  4. Java中定义常量方法及建议(Class/Interface)

    Class定义常量方法(推荐方法) //final修饰符 public final class Constants { //私有构造方法 private Constants() {} public s ...

  5. java中Integer常量池

    我们先看一个关于Integer的例子 public static void main(String[] args) { // TeODO Auto-generated method stu Integ ...

  6. Java中的常量池

    JVM中有: Class文件常量池.运行时常量池.全局字符串常量池.基本类型包装类对象 常量池 Class文件常量池: class文件是一组以字节为单位的二进制数据流,在java代码的编译期间,编写的 ...

  7. Java中定义常量(Constant) 的几种方法

    为了方便大家交流Spark大数据,浪尖建了微信群,目前人数过多,只能通过浪尖或者在群里的朋友拉入群.纯技术交流,偶有吹水,但是打广告,不提醒,直接踢出.有兴趣加浪尖微信. 常量使用目的 1,为什么要将 ...

  8. Java中赋值常量的注意事项

    写在前面: 从网上学习的赋值规则 摘录自网络,标明出处 byte,short,int,long 比如 int a = 234; 此处的常量234类型默认是int,如果要将大的整数赋值给变量的话 必须 ...

  9. 如何在Java中定义常量(Constant)

    原本引自  http://blog.csdn.net/autofei/article/details/6419460 /** * Method One */ interface ConstantInt ...

随机推荐

  1. c#中读取数据库bit布尔字段数据转换Int和bool时的错误

    数据库里bit这个布尔类型的字段,非常实用,但是在c#里读取时,许多人喜欢犯一些错误,导致运行报错. 实际中,有效的正确读取方法只有以下两种: int xxx= Convet.ToInt16(read ...

  2. JAVA函数的重载

    一.什么是重载(overlording) 在JAVA中,可以在同一个类中存在多个函数,函数名称相同但参数列表不同.这就是函数的重载(overlording).这是类的多太性表现之一. 二.重载的特征: ...

  3. canvas绘制圆形进度条(或显示当前已浏览网页百分比)

    使用canvas绘制圆形进度条,或者是网页加载进度条 或者是显示你浏览了本网页多少-- 由于个浏览器的计算差异,打开浏览器时 初始值有所不同,但是当拉倒网页底部时,均显示100%. 兼容性:测试浏览器 ...

  4. 【微信开发】玩转PHP 数组用法!

    数组的起始下标可以不从0开始,例子为从2开始. $data = array(2=>'A','B','C');     运行结果:$data = array(2=>'A',3=>'B' ...

  5. 谨慎能捕千秋蝉(三)——界面操作劫持与HTML5安全

    一.界面操作劫持 1)ClickJacking ClickJacking点击劫持,这是一种视觉上的欺骗. 攻击者使用一个透明的.不可见的iframe,覆盖在网页的某个位置上,诱使用户点击iframe. ...

  6. java Socket(TCP)编程小项目

    package 服务器端相关操作; import java.io.Serializable; /* * 创建存储需要传输信息的对象,方便客户端向服务器端传送数据 */ public class Cli ...

  7. Windows环境下安装配置Teamcity配合git自动发布mvc,webapi站点

    以下是本人配置Teamcity具体环境和步骤,只实现了项目发布,打包.Nodejs npm gulp没有配置成功,后期补上. 1 环境安装 本人使用的是windows7 sp1 64位系统,(.net ...

  8. Linux下ifort的安装记录

    首先进入网址https://software.intel.com/en-us/qualify-for-free-software/student 下载Intel Parallel Studio XE ...

  9. 利用busybox制作根文件系统

    实际项目中可以使用Buildroot制作根文件系统 1.busybox源码下载及配置 https://busybox.net/downloads/ 1.1.修改Makefile (1) ARCH = ...

  10. 对本地Solr服务器添加IK中文分词器实现全文检索功能

    在上一篇随笔中我们提到schema.xml中<field/>元素标签的配置,该标签中有四个属性,分别是name.type.indexed与stored,这篇随笔将讲述通过设置type属性的 ...