`
hkliya
  • 浏览: 85657 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

一个常量引发的思考

    博客分类:
  • j2ee
阅读更多

 

事出有因:

农历xx月xx日,是一个特别的日子,因为用户发邮件说:要将上传附件的大小从原来的5MB提高到10M。

这是多么简单多么合理的一个小变更,处理起来是多么的easy...

 

在代码中,我们是将这个数字定义为常量的:

 

public static final long MAX_SIZE = 5 * 1024 * 1024;
 

 

那修改也很简单,将5改成10即可。

但是当我们将新的class替换到系统上时,发现没有生效,再替换,还是不生效...

就在西二旗城铁站晚高峰快要结束的时候,突然,我灵光一闪,仿佛领悟到了什么,遂将引用这个常量的类也重新编译并替换到生产系统,It works!

 

由此我推测是因为编译器在编译时会对常量进行优化,将其替换为实际的值……

 

 

柳暗花明:

为了验证我的想法,我写了两个类来测试,

T1.java


 

T2.java

 


然后我编译,执行,删除T2.class,再执行。


有点出乎意料啊,和预想的结果并不一致……

就在二路汽车快要收工的时候,突然,我灵光又一闪,仿佛又领悟到了什么,遂将 Long 改成 long:


 

再次编译,执行,删除T2.class,再执行:



It works again!

 

后来又测试了int, Integer, double, Double, String等类型,

并且使用javap -c T1查看反编译结果发现:

 

编译器在编译时会对基本数据类型和String类型进行优化

 

 

最佳实践:

当系统在线上运行时,如果需要修改某个的常量值(基本类型或String类型),那么引用该常量的所有class都应当重新编译。换句话说:如果A类中使用了B类中的常量(基本类型或String类型),那在系统运行的过程中,你删除掉B.class,不会影响系统的使用。

 

所以如果你不想在后期维护时这么麻烦的话,最好定义变量时使用包装类(Long, Integer等),或者换一种设计方式。

 

比如原来的代码是这样:

HttpUtil类中有一个保存附件的方法

 

public class HttpUtil {
     public static final long MAX_SIZE = 5 * 1024 * 1024;

     public static void saveFile(HttpServletRequest req, long maxSize) {...}
}
 

 

在别的模块如信息发布中有上传附件的功能,会调用HttpUtil的保存文件方法

因为没有对附件的特殊限制,所以使用默认的MAX_SIZE即可:

 

…
HttpUtil.save(req, HttpUtil.MAX_SIZE);
…
 

 

这样的代码就存在前面提到的问题,如果修改常量值就需要重新编译引用常量的类。

但我们可以换一种方式,

在HttpUtil类中增加一个重载方法:

 

public static void saveFile(HttpServletRequest req) {
     saveFile(req, MAX_SIZE);
}
 

 

这样在别的模块需要上传附件并且仅使用默认文件大小限制的时候,就可以调用这个方法。

好处就是现在你再修改MAX_SIZE的值,只需要编译这一个类就可以了。

 

  • 大小: 16.7 KB
  • 大小: 11.5 KB
  • 大小: 32.3 KB
  • 大小: 12.5 KB
  • 大小: 12.8 KB
8
4
分享到:
评论
12 楼 hkliya 2011-12-07  
lujiawu12 写道
最初的: public static final Long MAX_SIZE = 5 * 1024 * 1024;   为什么修改不成功?

是在编译过程中做了解箱吗? 不然按您后来的逻辑应该成功才对。。。

看到最后就这点困惑。。。呵呵

有收获

谢谢,看的很认真呢。
前面是我写错了,应该是long而不是Long
11 楼 lujiawu12 2011-12-07  
最初的: public static final Long MAX_SIZE = 5 * 1024 * 1024;   为什么修改不成功?

是在编译过程中做了解箱吗? 不然按您后来的逻辑应该成功才对。。。

看到最后就这点困惑。。。呵呵

有收获
10 楼 hiker424 2011-12-07  
看来这2路汽车帮了大忙了,哈哈,顶
9 楼 xiaolv 2011-12-07  
略有小悟   谢了!
8 楼 wangyongjun0901 2011-12-07  
受教了!感谢楼主!
7 楼 chenzhou123520 2011-12-07  
不错,之前没有考虑过这个,楼主挺细心的
6 楼 hkliya 2011-12-07  
stef831018 写道
一个建议:这种配置用途的常量最好放到properties或者xml文件中去,增加一个文件-内存的同步,如果修改了文件,重新刷一遍内存即可,否则,像楼主所说的必须重新编译class文件; 其实主要是因为普通的基本类型常量以及String的常量是在预编译期间直接写到class里的,如果修改必须重新替换。
但是您提到用包装类去实现常量的功能,我真的不建议这样做,因为仅仅为了一个常量问题增加额外的对象在堆上没有太多必要,效率不高;以上个人意见...
另外我也有赶西二旗地铁的习惯,呵呵呵...

谢谢,有道理,我们也通常是写到properties文件中的,但没做内存和文件的同步。
其实这种配置写到数据库也行,用户量不大,上传文件操作不频繁的情况下。
5 楼 stef831018 2011-12-07  
一个建议:这种配置用途的常量最好放到properties或者xml文件中去,增加一个文件-内存的同步,如果修改了文件,重新刷一遍内存即可,否则,像楼主所说的必须重新编译class文件; 其实主要是因为普通的基本类型常量以及String的常量是在预编译期间直接写到class里的,如果修改必须重新替换。
但是您提到用包装类去实现常量的功能,我真的不建议这样做,因为仅仅为了一个常量问题增加额外的对象在堆上没有太多必要,效率不高;以上个人意见...
另外我也有赶西二旗地铁的习惯,呵呵呵...
4 楼 hkliya 2011-12-06  
sunyzc 写道
是因为那个常量值运行之后存储在内存里的吧?

不是的,编译完就删除掉T2.class,T1一样能运行
通过反编译也能看出来
3 楼 yoyo837 2011-12-06  
虽然我习惯把成员变量定义为包装类型,但是也是出于习惯而已,还真不确定基本类型与String的变量虚拟机都搞了什么把戏...
2 楼 ansjsun 2011-12-06  
没遇到过..不过看了这个文章遇到这个问题我就不怕不怕啦
1 楼 sunyzc 2011-12-06  
是因为那个常量值运行之后存储在内存里的吧?

相关推荐

    像计算机科学家一样思考 c++.

    《像计算机科学家一样思考C++》是一本旨在帮助读者深入理解C++编程语言,并通过计算机科学家的思维方式来学习和解决问题的教程。这本书的核心理念是,掌握C++不仅仅是学习语法和函数,更是要学会如何思考问题,如何...

    北大青鸟 深入.NET平台和C#编程 教学资料 PPT3/9

    例如,在Compute类中,pi是一个常量,用作计算圆的周长和面积的基础,定义常量时通常使用const关键字,常量名通常采用全大写以示区别。 枚举(enum)是另一种数据类型,它提供了一种定义一组相关值的方式,并为这些...

    2--实验2.doc

    源文件通常以类名命名,且每个类应对应一个单独的`.java`文件。 思考题涉及了源文件与字节码文件的区别,它们的扩展名分别是`.java`和`.class`。源文件包含了人类可读的Java代码,而字节码文件是编译后的机器可执行...

    《环境海洋学》思考题.pdf

    【环境海洋学】是研究海洋环境的科学,涵盖了海洋生态系统、海洋污染、气候变化与海洋的关系等多个方面。全球环境问题的成因主要包括滥采滥用自然资源、有害物质排放、城市人口膨胀和大型工程不当建设,这些行为导致...

    嵌入式工程师面试.docx

    例如,声明一个整型变量、一个指向整型的指针、一个指向指针的指针等,这些都需要候选人准确地理解和运用C语言的声明规则。 总的来说,嵌入式工程师的面试涵盖了预处理器、宏定义、循环控制、数据声明等多个关键...

    经典嵌入式面试题经典嵌入式面试题.doc

    ` 定义了一个指向指针的指针,该指针指向整型数。 - d) `int a[10];` 定义了一个包含10个整型数的数组。 - e) `int *a[10];` 定义了一个包含10个指向整型数的指针的数组。 6. **嵌入式系统特性**: - 在嵌入式...

    编程精粹理解,对于C语音编程安全无错的总结和思考

    为了避免这种情况,应创建一个专门的函数来管理内存重分配,这样可以确保在失败时正确释放原有的内存。 避免编写面面俱到的函数,比如realloc()函数,因为它需要处理多种边界情况,可能引起混淆和潜在错误。应将...

    高中信息技术《VB运算符与表达式》优质课教学设计、教案.pdf

    2. **VB 表达式的概念**:在 VB 中,表达式是由常量、变量、函数和运算符组成的,它们可以被计算以产生一个值。例如,2 + 3 是一个简单的算术表达式,它的结果是 5。 3. **VB 常用运算符**:包括算术运算符(如加、...

    人教五年级数学上册用字母表示数例例学习教案.pptx

    这篇文档是针对人教版小学五年级数学上册的一个教学教案,主要讲解如何用字母来表示数的概念。在数学中,使用字母表示数是一种抽象思维的体现,它可以帮助我们更简洁地表达和解决数学问题。 首先,课程通过日常生活...

    python简明教程.chm

    挑选一个编辑器 使用源文件 输出 它如何工作 可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 ...

    简明python教程(chm)

    挑选一个编辑器 使用源文件 输出 它如何工作 可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 ...

    简明python教程

    挑选一个编辑器 使用源文件 输出 它如何工作 可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 缩进 ...

    VB语言及程序开发环境汇编PPT教学课件.pptx

    课程的教学过程包含了多个环节,如创设情境,引发学生兴趣;探究活动,让学生通过实际操作理解概念;交流评价,促进合作与反思;操作实践,增强动手能力;课堂小结,巩固所学知识。每个环节都设计有具体的活动,如...

    128个c程式范例的光碟

    【压缩包子文件的文件名称列表】"c128"可能代表128个C语言的示例程序,每一个都对应一个特定的编程任务或概念。这些范例可能按照难度或主题分类,例如基础语法、数据结构、控制流程、函数、指针、内存管理、文件操作...

    C++基础面试题.docx

    ="比较,应设置一个容差值如EPSINON,用">="/"比较。 - 指针:用if(p==NULL)或if(p!=NULL)判断是否为空。 7. sizeof的计算: - 在不同情况下,sizeof的结果会因类型和环境而异,例如数组在函数参数中会退化为指针...

    嵌入式编程时需要留意的问题

    3. **预处理器指令#error**:`#error`是一个预处理指令,用于在编译时强制引发错误,通常用于调试或自定义编译时检查。了解这个指令的存在可以反映程序员对C语言的深入理解。 4. **死循环( Infinite loops)**:在...

    一道经典的C面试题+答案.pdf

    2. 对于float类型的x,比较零值时应引入一个极小的误差范围,如`const float EPSILON = 0.00001; if ((x >= -EPSILON) && (x ))`。避免直接使用`==`或`!=`与0.0比较,因为浮点数比较可能会导致精度问题。 3. 对于...

    一次函数的图像与性质教学设计分享.pdf

    教学流程设计应包含情境导入,如通过小明和爸爸赛跑的问题引发函数解析式的讨论,复习正比例函数和一次函数的定义,引导学生认识到图像在解决问题中的作用。接着,通过实验探究正比例函数的图像和性质,让学生自己...

Global site tag (gtag.js) - Google Analytics