[改善Java代码]不要只替换一个类
建议20: 不要只替换一个类
我们经常在系统中定义一个常量接口(或常量类),以囊括系统中所涉及的常量,从而简化代码,方便开发,在很多的开源项目中已采用了类似的方法,比如在Struts2中,org.apache.struts2.StrutsConstants就是一个常量类,它定义了Struts框架中与配置有关的常量,而org.apache.struts2.StrutsStatics则是一个常量接口,其中定义了OGNL访问的关键字。
关于常量接口(类)我们来看一个例子,首先定义一个常量类:
public class Constant {
//定义人类寿命极限
public final static int MAX_AGE = 150;
}
public class Client {
public static void main(String[] args) {
System.out.println("人类寿命极限是:" + Constant.MAX_AGE);
}
}
运行的结果非常简单(结果省略)。目前的代码编写都是在“智能型”IDE工具中完成的,下面我们暂时回溯到原始时代,也就是回归到用记事本编写代码的年代,然后看看会发生什么奇妙事情(为什么要如此,稍后会给出答案)。
修改常量Constant类,人类的寿命增加了,最大能活到180岁,代码如下:
public class Constant {
//定义人类寿命极限
public final static int MAX_AGE = 180;
}
然后重新编译:javac Constant,编译完成后执行:java Client,(只执行Client类,不编译)大家想看看输出的极限年龄是多少岁吗?
输出的结果是:“人类寿命极限是:150”,竟然没有改变为180,太奇怪了,这是为何?
原因是:对于final修饰的基本类型和String类型,编译器会认为它是稳定态(Immutable Status),所以在编译时就直接把值编译到字节码中了,避免了在运行期引用(Run-time Reference),以提高代码的执行效率。针对我们的例子来说,Client类在编译时,字节码中就写上了“150”这个常量,而不是一个地址引用,因此无论你后续怎么修改常量类,只要不重新编译Client类,输出还是照旧。
而对于final修饰的类(即非基本类型),编译器认为它是不稳定态(Mutable Status),在编译时建立的则是引用关系(该类型也叫做Soft Final),如果Client类引入的常量是一个类或实例,即使不重新编译也会输出最新值。
千万不可小看了这点知识,细坑也能绊倒大象,比如在一个Web项目中,开发人员修改一个final类型的值(基本类型),考虑到重新发布风险较大,或者是时间较长,或者是审批流程过于繁琐,反正是为了偷懒,于是直接采用替换class类文件的方式发布。替换完毕后应用服务器自动重启,然后简单测试一下(比如本类引用final类型的常量),一切OK。可运行几天后发现业务数据对不上,有的类(引用关系的类)使用了旧值,有的类(继承关系的类)使用的是新值,而且毫无头绪,让人一筹莫展,其实问题的根源就在于此。
恩,还有个小问题没有说明,我们的例子为什么不在IDE工具(比如Eclipse)中运行呢?那是因为在IDE中不能重现该问题,若修改了Constant类,IDE工具会自动编译所有的引用类,“智能”化屏蔽了该问题,但潜在的风险其实仍然存在。
注意 发布应用系统时禁止使用类文件替换方式,整体WAR包发布才是万全之策。
[改善Java代码]不要只替换一个类的更多相关文章
- [改善Java代码]异常只为异常服务
异常原本是正常逻辑的补充,但是有时候会被当做主逻辑使用.看如下代码: public class Client { enum Color { Red, Blue; } public static voi ...
- [改善Java代码]注意Class类的特殊性
Java语言是先把Java源文件编译成后缀为class的字节码文件,然后再通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行的,这是Java处理的基本机制,但加载到内存中的数据是 ...
- 为什么java源文件中只允许一个public类存在
1.提出问题 为什么java源文件中只允许一个public类存在? 2.分析问题 问题涉及到的条件:源文件的名字 public类 main方法 一般我们在编写一个源文件的时候: 一个pu ...
- java多线程并发去调用一个类的静态方法安全性探讨
java多线程并发去调用一个类的静态方法安全性探讨 转自:http://blog.csdn.net/weibin_6388/article/details/50750035 这篇文章主要讲多线程对 ...
- Java中是否可以调用一个类中的main方法?
前几天面试的时候,被问到在Java中是否可以调用一个类中的main方法?回来测试了下,答案是可以!代码如下: main1中调用main2的主方法 package org.fiu.test; impor ...
- Javascript replace 为什么只替换一个字符?
Javascript replace 为什么只替换一个字符? 如下代码,为什么结果是 "a2b1c1" ? 'a1b1c1'.replace('1', 2); 因为 javascr ...
- java代码行数统计工具类
package com.syl.demo.test; import java.io.*; /** * java代码行数统计工具类 * Created by 孙义朗 on 2017/11/17 0017 ...
- [改善Java代码]让工具类不可实例化
建议42: 让工具类不可实例化 Java项目中使用的工具类非常多,比如JDK自己的工具类java.lang.Math.java.util.Collections等都是我们经常用到的.工具类的方法和属性 ...
- [改善Java代码]使用package-info类为包服务
建议50: 使用package-info类为包服务 Java中有一个特殊的类:package-info类,它是专门为本包服务的,为什么说它特殊呢?主要体现在3个方面: (1)它不能随便被创建 在一般的 ...
随机推荐
- Spark的应用程序
Spark的应用程序,分为两部分:Spark driver 和 Spark executor.
- HDU 5723 Abandoned country (最小生成树 + dfs)
Abandoned country 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5723 Description An abandoned coun ...
- memcached全面剖析–4. memcached的分布式算法
memcached的分布式 正如第1次中介绍的那样, memcached虽然称为“分布式”缓存服务器,但服务器端并没有“分布式”功能. 服务器端仅包括 第2次. 第3次 前坂介绍的内存存储功能,其实现 ...
- HDU1398Square Coins(母函数)
母函数介绍见另一篇随笔HDU1028Ignatius and the Princess III(母函数) #include<iostream> #include<stdio.h> ...
- Unity3D之空间转换学习笔记(二):基础数学
这期笔记我们专注Unity提供的各种数学相关的类来学习. 时间Time API文档地址:http://docs.unity3d.com/ScriptReference/Time.html 时间加/减速 ...
- 单个SWF文件loading加载详解(转)
通过带宽查看器,可以看到SWF中每帧所占带宽状况.另外,我们还可以在Flash发布设置中,选择生成体积报告. 勾选这一项之后,发布flash时,会自动在fla目录中生成一个名为”文件名 Report. ...
- 让java程序在后台一直执行(例如putty关闭后后台程序继续运行)
如果在终端中执行java -jar xxx.jar&命令,当终端关闭时,xxx.jar也会同时结束运行,但是如果执行nohup java -jar xxx.jar&命令,则程序会在后台 ...
- 169 Majority Element [LeetCode Java实现]
题目链接:majority-element /** * Given an array of size n, find the majority element. The majority elemen ...
- Xcode8中Swift3.0适配问题
写在前面 收到一些小伙伴的来信,觉得可能下边没有表达清楚,先把大家关心的要点在此进行总结,有兴趣的可以看看下边的研究过程,没兴趣的直接看这段即可. Xcode8支持Swift2.3和Swift3.0两 ...
- currentTarget 与 Target 的区别
在一般情况下,target与currentTarget指向的是同一个对象.一般情况是指我们只对某一个独立的mc添加侦听器.如下: var mc:Sprite=new Sprite();addChild ...