建议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代码]不要只替换一个类的更多相关文章

  1. [改善Java代码]异常只为异常服务

    异常原本是正常逻辑的补充,但是有时候会被当做主逻辑使用.看如下代码: public class Client { enum Color { Red, Blue; } public static voi ...

  2. [改善Java代码]注意Class类的特殊性

    Java语言是先把Java源文件编译成后缀为class的字节码文件,然后再通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行的,这是Java处理的基本机制,但加载到内存中的数据是 ...

  3. 为什么java源文件中只允许一个public类存在

    1.提出问题 为什么java源文件中只允许一个public类存在? 2.分析问题 问题涉及到的条件:源文件的名字    public类     main方法 一般我们在编写一个源文件的时候: 一个pu ...

  4. java多线程并发去调用一个类的静态方法安全性探讨

    java多线程并发去调用一个类的静态方法安全性探讨 转自:http://blog.csdn.net/weibin_6388/article/details/50750035   这篇文章主要讲多线程对 ...

  5. Java中是否可以调用一个类中的main方法?

    前几天面试的时候,被问到在Java中是否可以调用一个类中的main方法?回来测试了下,答案是可以!代码如下: main1中调用main2的主方法 package org.fiu.test; impor ...

  6. Javascript replace 为什么只替换一个字符?

    Javascript replace 为什么只替换一个字符? 如下代码,为什么结果是 "a2b1c1" ? 'a1b1c1'.replace('1', 2); 因为 javascr ...

  7. java代码行数统计工具类

    package com.syl.demo.test; import java.io.*; /** * java代码行数统计工具类 * Created by 孙义朗 on 2017/11/17 0017 ...

  8. [改善Java代码]让工具类不可实例化

    建议42: 让工具类不可实例化 Java项目中使用的工具类非常多,比如JDK自己的工具类java.lang.Math.java.util.Collections等都是我们经常用到的.工具类的方法和属性 ...

  9. [改善Java代码]使用package-info类为包服务

    建议50: 使用package-info类为包服务 Java中有一个特殊的类:package-info类,它是专门为本包服务的,为什么说它特殊呢?主要体现在3个方面: (1)它不能随便被创建 在一般的 ...

随机推荐

  1. Spring Autowiring by Constructor

    In Spring, "Autowiring by Constructor" is actually autowiring by Type in constructor argum ...

  2. Quality Center11初始化失败

    打开start_a.jsp页面总是闪退,原因如下: 初始化失败因为证书签名过期了.把IE选项里证书检查的三项勾掉就好了(检查发行商的证书是否吊销.检查服务器证书吊销.检查已下载的程序的签名)

  3. ASP.NET中身份验证的三种方法

    Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活.Forms 验证方式对基于用户的验证授权 ...

  4. Java NIO原理和使用(转载一)

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内 ...

  5. 树莓派加入定时任务实现花生壳定时重启(linux的定时任务)

    由于花生壳在linux下不稳定,联系开机一个星期左右会挂掉,所以要使用定时任务实现每小时刷新一次/启动一次. 使用的是linux下的定时任务crontab去实现. 实现步骤: 1.编辑/etc/cro ...

  6. jquery formValidate demo 采用struts 异步方式检验用户名是否存在

    1 login.jsp <%@taglib uri="/struts-tags" prefix="s"%><!DOCTYPE html PUB ...

  7. CentOS6.4 64位系统安装jdk

    1. CentOS操作安装好了以后,系统自带了openJDK,先查看相关的安装信息: $rpm -qa | grep java tzdata-java-2013b-1.el6.noarchjava-1 ...

  8. VMM服务模板(虚机、APP)部署排错

    I won't focus this blog on how to create a service template but more on how you can track the change ...

  9. Codeforces Gym 100513M M. Variable Shadowing 暴力

    M. Variable Shadowing Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100513/ ...

  10. pjsip视频通信开发(上层应用)之拨号界面整体界面功能实现

    在前面的几章里面写了显示.键盘.拨号.删除功能,这里我将他们进行组合,形成一个拨号键盘全部功能.首先是布局 <LinearLayout xmlns:android="http://sc ...