Infoq: Java编码易疏忽的十个问题

在Java编码中,我们容易犯一些错误,也容易疏忽一些问题,因此笔者对日常编码中曾遇到的一些经典情形归纳整理成文,以共同探讨。

1. 纠结的同名

现象

很多类的命名相同(例如:常见于异常、常量、日志等类),导致在import时,有时候张冠李戴,这种错误有时候很隐蔽。因为往往同名的类功能也类似,所以IDE不会提示warn。

解决

写完代码时,扫视下import部分,看看有没有不熟悉的。替换成正确导入后,要注意下注释是否也作相应修改。

启示

命名尽量避开重复名,特别要避开与JDK中的类重名,否则容易导入错,同时存在大量重名类,在查找时,也需要更多的辨别时间。

2. 想当然的API

现象

有时候调用API时,会想当然的通过名字直接自信满满地调用,导致很惊讶的一些错误:

示例一:flag是true?

boolean flag = Boolean.getBoolean("true");

可能老是false。

示例二:这是去年的今天吗(今年是2012年,不考虑闰年)?结果还是2012年:

Calendar calendar = GregorianCalendar.getInstance();
calendar.roll(Calendar.DAY_OF_YEAR, -365);

下面的才是去年:

calendar.add(Calendar.DAY_OF_YEAR, -365); 

解决办法

问自己几个问题,这个方法我很熟悉吗?有没有类似的API? 区别是什么?就示例一而言,需要区别的如下:

Boolean.valueOf(b) VS Boolean.parseBoolean(b) VS Boolean.getBoolean(b);

启示

名字起的更详细点,注释更清楚点,不要不经了解、测试就想当然的用一些API,如果时间有限,用自己最为熟悉的API。

3. 有时候溢出并不难

现象

有时候溢出并不难,虽然不常复现:

示例一:

long x=Integer.MAX_VALUE+1;
System.out.println(x);

x是多少?竟然是-2147483648,明明加上1之后还是long的范围。类似的经常出现在时间计算:

数字1×数字2×数字3… 

示例二:

在检查是否为正数的参数校验中,为了避免重载,选用参数number, 于是下面代码结果小于0,也是因为溢出导致:

Number i=Long.MAX_VALUE;
System.out.println(i.intValue()>0);

解决

  1. 让第一个操作数是long型,例如加上L或者l(不建议小写字母l,因为和数字1太相似了);
  2. 不确定时,还是使用重载吧,即使用doubleValue(),当参数是BigDecimal参数时,也不能解决问题。

启示

对数字运用要保持敏感:涉及数字计算就要考虑溢出;涉及除法就要考虑被除数是0;实在容纳不下了可以考虑BigDecimal之类。

4. 日志跑哪了?

现象

有时候觉得log都打了,怎么找不到?

示例一:没有stack trace!

 } catch (Exception ex) {
    log.error(ex);
 }

示例二:找不到log!

} catch (ConfigurationException e) {
    e.printStackTrace();
}

解决

  1. 替换成log.error(ex.getMessage(),ex);
  2. 换成普通的log4j吧,而不是System.out。

启示

  1. API定义应该避免让人犯错,如果多加个重载的log.error(Exception)自然没有错误发生
  2. 在产品代码中,使用的一些方法要考虑是否有效,使用e.printStackTrace()要想下终端(Console)在哪。

5. 遗忘的volatile

现象

在DCL模式中,总是忘记加一个Volatile。

private static CacheImpl instance;  //lose volatile
public static CacheImpl getInstance() {
    if (instance == null) {
        synchronized (CacheImpl.class) {
            if (instance == null) {
                instance = new CacheImpl (); 
            }
        }
    }
    return instance;
}

解决

毋庸置疑,加上一个吧,synchronized 锁的是一块代码(整个方法或某个代码块),保证的是这”块“代码的可见性及原子性,但是instance == null第一次判断时不再范围内的。所以可能读出的是过期的null。

启示

我们总是觉得某些低概率的事件很难发生,例如某个时间并发的可能性、某个异常抛出的可能性,所以不加控制,但是如果可以,还是按照前人的“最佳实践”来写代码吧。至少不用过多解释为啥另辟蹊径。

6. 不要影响彼此

现象

在释放多个IO资源时,都会抛出IOException ,于是可能为了省事如此写:

public static void inputToOutput(InputStream is, OutputStream os,
           boolean isClose) throws IOException {
    BufferedInputStream bis = new BufferedInputStream(is, 1024);
    BufferedOutputStream bos = new BufferedOutputStream(os, 1024);  
    ….
    if (isClose) {
       bos.close();
       bis.close();
    }
}

假设bos关闭失败,bis还能关闭吗?当然不能!

解决办法

虽然抛出的是同一个异常,但是还是各自捕获各的为好。否则第一个失败,后一个面就没有机会去释放资源了。

启示

代码/模块之间可能存在依赖,要充分识别对相互的依赖。

7. 用断言取代参数校验

现象

如题所提,作为防御式编程常用的方式:断言,写在产品代码中做参数校验等。例如:

private void send(List< Event> eventList)  {
    assert eventList != null;
}

解决

换成正常的统一的参数校验方法。因为断言默认是关闭的,所以起不起作用完全在于配置,如果采用默认配置,经历了eventList != null结果还没有起到作用,徒劳无功。

启示

有的时候,代码起不起作用,不仅在于用例,还在于配置,例如断言是否启用、log级别等,要结合真实环境做有用编码。

8. 用户认知负担有时候很重

现象

先来比较三组例子,看看那些看着更顺畅?

示例一:

public void caller(int a, String b, float c, String d) {
    methodOne(d, z, b);
    methodTwo(b, c, d);
}
public void methodOne(String d, float z, String b)  
public void methodTwo(String b, float c, String d)

示例二:

public boolean remove(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);
public boolean delete(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);

示例三:

public static String getDigest(String filePath, DigestAlgorithm algorithm)
public static String getDigest(String filePath, DigestAlgorithm digestAlgorithm)

解决

  1. 保持参数传递顺序;
  2. remove变成了delete,显得突兀了点, 统一表达更好;
  3. 保持表达,少缩写也会看起来流畅点。

启示

在编码过程中,不管是参数的顺序还是命名都尽量统一,这样用户的认知负担会很少,不要要用户容易犯错或迷惑。例如用枚举代替string从而不让用户迷惑到底传什么string, 诸如此类。

9. 忽视日志记录时机、级别

现象

存在下面两则示例:

示例一:该不该记录日志?

catch (SocketException e)
{
    LOG.error("server error", e);
    throw new ConnectionException(e.getMessage(), e);
}   

示例二:记什么级别日志?

在用户登录系统中,每次失败登录:

LOG.warn("Failed to login by "+username+");

解决

  1. 移除日志记录:在遇到需要re-throw的异常时,如果每个人都按照先记录后throw的方式去处理,那么对一个错误会记录太多的日志,所以不推荐如此做;但是如果re-throw出去的exception没有带完整的trace( 即cause),那么最好还是记录下。
  2. 如果恶意登录,那系统内部会出现太多WARN,从而让管理员误以为是代码错误。可以反馈用户以错误,但是不要记录用户错误的行为,除非想达到控制的目的。

启示

日志改不改记?记成什么级别?如何记?这些都是问题,一定要根据具体情况,需要考虑:

  1. 是用户行为错误还是代码错误?
  2. 记录下来的日志,能否能给别人在不造成过多的干扰前提下提供有用的信息以快速定位问题。

10. 忘设初始容量

现象

在JAVA中,我们常用Collection中的Map做Cache,但是我们经常会遗忘设置初始容量。

cache = new LRULinkedHashMap< K, V>(maxCapacity);

解决

初始容量的影响有多大?拿LinkedHashMap来说,初始容量如果不设置默认是16,超过16×LOAD_FACTOR,会resize(2 * table.length),扩大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable),即整个数组Copy, 那么对于一个需要做大容量CACHE来说,从16变成一个很大的数量,需要做多少次数组复制可想而知。如果初始容量就设置很大,自然会减少resize, 不过可能会担心,初始容量设置很大时,没有Cache内容仍然会占用过大体积。其实可以参考以下表格简单计算下, 初始时还没有cache内容, 每个对象仅仅是4字节引用而已。

  • memory for reference fields (4 bytes each);
  • memory for primitive fields
Java type Bytes required
boolean 1
byte
char 2
short
int 4
float
long 8
double

启示

不仅是map, 还有stringBuffer等,都有容量resize的过程,如果数据量很大,就不能忽视初始容量可以考虑设置下,否则不仅有频繁的 resize还容易浪费容量。

在Java编程中,除了上面枚举的一些容易忽视的问题,日常实践中还存在很多。相信通过不断的总结和努力,可以将我们的程序完美呈现给读者。

Paper: 远程混合学习方式及支持平台构建方案研究

 

傅健1  杨雪1  陆绍圆2

(1吉林大学高等教育研究所吉林长春130012  2无锡联企网络科技有限公司 江苏无锡214000)

【摘要】新兴的移动学习与传统的网络学习具有很强的互补性,可以预见传统的网络学习与移动学习的混合使用将成为未来远程学习的主流方式。提出两种学习方式的混合形式和三个发展阶段,并对目前可行的支持平台的构建方案进行分析,为远程学习的进一步发展提供参考。

【关键词】混合学习,移动学习,传统网络学习,平台构建

中图分类号              文献标识码              论文编号

Reserch for Distance Blend-learning and Supportting Platform Construction’s Method

FU Jian1   YANG Xue1  LU Shao-yuan2

1 Institute of Higher Education, Jilin University, Jilin,Changchun, 130012, China;2 Wuxi Cmswe Technology Co.,ltd,Jiangsun,WuXi,21400,China)

Abstruct: M-learning and traditional E-learning have more complementarity,so the blended learning conbine traditional E-learning and M-learning will become the leading learning style in the future.This article present the blending forms and three development phases based M-learning and E-learning,also analyzes the feasible technology methods to constrcut supportting platform,provides references for the development of distance learning.

Keywords: Blended learning, M-Learning, E-leraning, Platform construct

 

随着通信技术的发展和移动设备性能的提高,将手机、PDA等移动设备应用于学习,不仅能提高学习效率而且更加新颖时尚,WAP浏览、短信收发等多种可选方式,使其成为远程学习的有效补充,但目前移动学习所受局限性较大,表现在屏幕尺寸、终端性能、可利用资源等诸多方面,而相对而言,传统网络学习虽然没有移动学习便捷,但技术应用较为成熟,资源丰富,学生认可度也较高,因此在提倡移动学习方式的同时,若能结合传统网络学习已有理论、实践成果及优势来弥补移动学习当前或与生俱来的“不足”,可以构建较好的远程学习方式,通过分析传统网络学习与移动学习的互补性及结合的教育应用价值,进而提出将传统网络学习与移动学习两种学习方式混合的方式及具体方案,为远程学习方式研究与实践者提出参考。

  移动学习与传统网络学习互补性分析

目前移动学习的定义版本很多,定义的角度涉及到技术、学习等多方面,但较为认同的是“移动学习是一种在移动计算设备帮助下的能够在任何时间、任何地点可以进行的学习, 移动学习所使用的移动计算设备必须能够有效地呈现学习内容, 并提供教师与学习者之间的双向交流”。[1]

移动学习,是传统网络学习的有效扩展和补充,它与传统网络学习以是否使用移动设备来划分,两种学习方式具体特性比较见表1:

表1 传统网络学习与移动学习比较

  传统网络学习 移动学习
学习类型 正式学习 非正式学习
主要面向对象 在校学生 工作学习时间分散的人群,追求新颖的青少年
使用设备 普通电脑:笨重 手机、UMPC、PDA等移动设备:轻,便捷
学习时间 时间可较集中,但对时间

无缝学习支持不好

24×7可随时按需学习
学习地点 教室、机房、私人场所等室内 无线网络覆盖地
资源开发难度与成本 容易,费用稍低 终端多样性及局限性等导致开发难度大,开发费用高
学习感 较熟悉 “个人拥有”感较强、新颖时尚
对情景学习的支持 对真实场景下的学习支持较差 移动性好,更支持真实情景学习与体验式学习
学习方式特点 稳定、不灵活、资源丰富 不稳定、灵活、资源不丰富
学习效果评价 容易 学习者分散等导致学习效果跟踪难,不易评价
当前主要应用形式 网课等 成人英语学习(如行学一族)、教学管理、师生、生生短信交流

从上图可以看出,移动学习与传统网络学习具有较强的互补性,若将两种学习方式结合,则具有以下优势:

(1)应用时空的互补性:传统网络学习可用于集中时间的学习,多在教室机房或私人场所进行,效率较高,但是对特定情景下的学习支持欠佳,而移动学习多发生在零散的时间和非正式的场合,虽效率不高,但对情景学习、体验学习支持较好,两者互为补充;

(2)当前应用的互补性:传统网络学习资源及平台功能完善,终端性能也较好,适合做复杂的功能处理,但却不便捷,移动学习的学习设备虽然局限性较大,但较为便捷;

(3)学习“感受”的互补性:传统网络学习使学生具有更强的“熟悉感”和“支持感”,移动学习下,移动设备使学生获得更强的“拥有感”和“自主感”;

(4)主要面向对象的互补性:传统网络学习要求时间较为集中,因此对于在校学生更适合,而对于在职成人等学习时间分散的学习者而言,移动学习更为适合。

综上,将移动学习和传统网络学习结合,可实现功能互补的教育应用,满足随时随地按需学习需求,例如:对于室外作业人员,工作中可以用手机随时记录自己的工作“心得”,工作之余时间较宽裕情况下则可在普通电脑上接入互联网来集中整理日常零散收集的的“心得”,同时,也可以将自己的“心得”筛选加工后作为日后移动学习的材料。下面介绍如何将两种学习方式进行混合以支持类似教育应用。

  移动学习与传统网络学习混合的阶段及方式

移动学习方式主要包括短信方式、WAP( Wireless Application Protocol,即无线应用协议)浏览方式和非标准移动学习(无线网络方式),而传统网络学习方式是指在普通PC机上进行的Web页面浏览方式,因此可将移动学习与传统网络学习按照具体学习方式的混合进行结合,按照混合程度的高低可划分为三个阶段(如图1):

111

图1 传统网络学习与移动学习混合方式及阶段

1 组合阶段:即提倡两种学习方式一起使用,但两者相应的支持平台没有直接联系,这种方式没有统一的平台,因此混合度较低,学习缺乏设计,学习效果欠佳;

2 集成阶段:作为标准的移动学习,主要是短信方式和WAP浏览方式,而传统网络学习是以WEB站点浏览方式实现,因此在集成阶段,需要一个混合平台能在保证数据统一情况下支持短信方式/WAP浏览方式和WEB站点的混合,具体如下:

(1)基于WAP和WEB的混合平台:手机等移动终端通过WAP页面(即WML等)浏览的方式访问平台,而普通PC通过传统WEB页面浏览访问平台,目前较常见的混合方案是使用XML技术开发移动设备及PC内容自适应的平台;

(2)基于SMS和WEB的混合平台:短信方式(SMS,EMS,MMS等)的学习不仅可以传递学习内容、教务信息,还可以作为师生、生生之间情感交流的重要工具,但短信学习与传统网络学习相比不易跟踪管理,因此将其与传统网络学习混合,开发整合短信功能的WEB平台,通过短信方式将网页与学习者联系,更易跟踪与管理,也便于突破传统的短信群发学习方式,体现个性化学习;

3 统一阶段:主要以无线网络方式的实现为主,即使用笔记本、UMPC等高性能移动设备学习传统网络资源,这种方式虽然能支持两种学习方式,但目前对移动设备要求较高,对移动学习的优势体现不明显,初期投资成本较大,推广存在困难。

  支持平台构建方案关键技术探究

    从上面可知,将两种学习方式根据混合程度不同,可划分为不同的阶段,但就目前而言,集成阶段更能体现对传统网络学习与移动学习的混合支持,因此下面对该阶段的两种混合方式下支持平台的构建进行研究:

    1 基于WAPWEB的混合平台构建

构建支持多种类型终端的学习平台传统的方案往往是采用XML技术开发终端自适用的学习平台(如图2[2]),XML,即可扩展标记语言,作为一种标准通用标记语言,它是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具[3]。将学习平台开发成采用XML技术的通用平台,优势是可以自适用终端、开发效率高,但目前开发难度较大,且不区分客户端类型:同时给性能悬殊的客户端(手机、普通PC)提供相同功能,缺乏对传统网络学习和移动学习本身特点的考虑及优势利用,同时也不利于内容的针对性调整,当前开发与应用的“通用”网络课程更多的是以移动学习方式特点来开发,缺乏对传统网络学习的优势体现。

222

图2 XML技术原理

因此针对以上方案的弊端,笔者结合传统网络学习和移动学习本身特点及终端的的差异性提出“功能分流、分层开发、站点复用”的方案,该方案技术原理是:根据实际学习内容依据两者学习方式的特点将平台功能进行分化(例如将管理功能多由传统网络学习完成),然后采用分层开发技术开发可复用部分(如图2)”的两套站点,最后以“站点复用”(如图3)方式部署在同一服务器中,以分别面向移动学习和传统网络学习,这样的开发不仅能到达一定的复用(开发复用、站点复用),也能在保证数据统一的前提下根据移动学习和传统网络学习的特点及实际需要进行针对性设计和内容的调整,例如对于资源的管理功能可更多集成到传统网络学习接入方式下完成;而用于移动学习的内容,可以使用简洁的形式呈现,弱化传统网络学习一些管理功能,强调知识的易获取性,这样一定程度上强化了移动学习的易用性,该方案涉及以下两方面技术应用:

(1)开发复用的应用:开发复用是指在开发中可以对程序的代码或者数据库进行一定程度的复用,以JSP语言为例,如图3,若采用MVC方式,则在控制层和模型层及数据库都可以在一定程度复用,站点的区分交给视图层完成:使用JSP动态生成HTML或WML的方式来标识不同终端可访问页面,这种方式提高了开发的效率也保证了数据库数据的一致性。

333

图3 “开发复用”的应用

(2)站点复用的实现:如图4,在同一台服务器上架设不同的站点,可以节约成本,而且每个站点具有独立的虚拟实体,通过确保站点具有不同的标识符即可使用户能正确访问站点,通常采用主机头、IP、端口号三种标识符来标识,例如:使用主机头标识时,可用http://wap.xxx.com和http://web.xxx.com来分别标识移动学习与传统网络学习的访问地址。

444

图4 “站点复用”的应用

以上方案若在对系统要求接入和处理速率要求较高的情况下,还需要进一步的强化:一般情况下,如图5(左),可以将数据库存储到单独的服务器上,以提高数据调用速度和数据安全性,以上方案,若需进一步提高平台的页面访问速度,可以将Wap和Web站点部署到不同的服务器上,如图5(右),但这种方案虽提高页面速度却较低了复用效率,需考虑实际成本等条件,谨慎使用。

555

图5 优化的设计方案

小结:以上提出的方案,虽然较传统的XML方案部署成本有所提高,但开发难度较低,易于根据实际需要对内容进行调整,也易于体现两种学习方式本身的特点和优势,可作为一种参考。

2 基于SMSWEB的混合平台构建

短信息服务(SMS)以其及时性强、使用快捷成为移动学习主要学习方式之一,当前开发混合SMS功能的WEB平台(类似twitter[4]平台),实际上是在传统的网页式学习上加入短信收发功能,需从移动服务商租用短信接口,并对其进行调用,如图5所示,具备SMS短信功能的平台即可以让普通PC机进行浏览,也可以在短信网关帮助下通过调用URL方式,利用移动、联通等无线通信网络给注册学习者的手机等移动设备发送信息,相反,移动设备也通过与指定短信中心号码收发信息来将短信内容呈现在属于私人或公共的网页上。这样的混合应用可以利用传统的网络学习解决短信息内容学习难于跟踪和管理的缺点,也可以兼顾短信息学习的便捷优势。例如在知识管理上,用户工作时可以通过短信方式将自己的灵感性知识发送到指定号码,这样就可以在工作之余登录WEB平台,对已收的灵感知识进行集中处理和学习。

666

图5 混合SMS功能的WEB平台功能示意

结语

通过以上对移动学习和传统网络学习互补性分析及混合方式研究,即可将两种学习方式进行有效混合,通过相应的技术方案构建的混合学习平台,可以在技术上提供支持,无论是新兴的移动学习还是传统网络学习,都各有优势与弊端,在实际教育应用中,还应多将两者优势结合而不是顾此失彼,方能提高学习效率,可以预见,随着教育信息化步伐的推进,兼顾传统网络学习与移动学习的优势的混合学习将成为的远程学习的趋势。

参考文献

[1]叶成林,徐福荫,许骏.移动学习研究综述[J].电化教育研究,2004,(3):12-19.

[2]黄荣怀,Jyri Salomaa.移动学习-理论·现状·趋势[M].北京:科学出版社,2008

[3]孟祥光.基于移动学习的网络课程研究[D].北京:首都师范大学,2005,11-12

[4]冀鹏飞,江玲.基于Twitter的移动学习策略研究[M].现代教育技术,2008,(9):106-108.

 

作者简介:

傅健,1985年,男,安徽巢湖人,吉林大学高等教育研究所教育技术学硕士在读

杨雪,教授,辽宁大连市人,吉林大学高等教育研究所(130012)

陆绍圆,男,江苏无锡人,无锡联企网络科技有限公司(214000)