那些丑陋的Java API

Java语言自从1995年正式推出以来,已经成为被广泛采用的编程语言之一。在[url=http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html]TIOBE编程语言排行榜[/url]榜单上也一直保持着前几名的位置。

《Java: The Good Parts / Java语言精粹》一书中,SUN的工程师更是大加赞扬了Java在各个方面的优点,其实Java存在很多Bad Parts需要改善。有时候开发者只是需要一个很简单的支持,但是SUN(如今是Oracle)的大师们却需要我们死很多脑细胞才能实现我们想要的。

Tiago Fernandez曾经做过一个很有意思的投票,统计对Java API的不满意程度,EJB2.x、Date/Time/Calendar、XML/DOM、AWT/Swing、EJB3.x、Mail、Servlet/JSP、I/O等均上榜。让我们来看看这些“丑陋的Java API”吧!

[b](1)臭名昭著的EJB[/b]
这个就不多说了。虽然EJB3.x有所改善,但是开发者更能接受Spring了。

[b](2)日期处理[/b]
Java中的日期处理API是被所有的开发者深恶痛绝的!最初使用java.util.Date实现,包含了日期与时间,但是无法实现国际化。更奇怪的是属性的偏移量,月份与小时都是基于0,月份中的天数则是基于1,而年则是从1900开始的。而随着JDBC的发布,又诞生了一个java.sql.Date,它继承自java.util.Date,但却有不同的含义。IBM在1998年贡献了一个java.util.Calendar,实现了国际化,但却更复杂。

// Return Last Day of a Month "2013/4/8"
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2013);
calendar.set(Calendar.MONTH, 4 - 1);
calendar.set(Calendar.DATE, 8);
calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
// calendar.add(Calendar.DATE, -1);

Date dd = cal.getTime();

SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String lastDay = sdf.format(dd);


2005年,Stephen Colebourne发布Joda-Time库,这个库非常流行并且得到了广泛的使用。Stephen Colebourne领导的JSR 310: Date and Time API已经被添加到了Java 8的特性列表中,期待有所改善吧!

[url=http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result]http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result[/url]
[url=http://stackoverflow.com/questions/15629222/java-sql-timestamp-comparison-bug]http://stackoverflow.com/questions/15629222/java-sql-timestamp-comparison-bug[/url]
[url=http://stackoverflow.com/questions/13141123/java-sql-timestamp-created-from-java-util-date-why-always-before-it]http://stackoverflow.com/questions/13141123/java-sql-timestamp-created-from-java-util-date-why-always-before-it[/url]

[b](3)异常捕获[/b]
比如以下一个简单的反射获取字段值,需要捕获4个不同的异常:
Field field;
try {

field = getClass().getField("testField");
Object value = field.get(this);

} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

类似的API包括:反射API、RMI API、JDBC API、 JAXP等Xml相关API、J2EE相关API(JNDI/EJB2/JMS/JavaMail等)等等。
很多开发者不得不捕获所有的Exception和Throwable。

[url=http://xstream.codehaus.org/]XStream[/url] Thoughtworks的开源产品,采用的是xpp来进行xml和Java对象之间的转换。它不需要schema或其他的mapping文件就可以进行java对象和xml文件之间的转换,API调用起来非常方便,并且扩展功能强大。

Java 7 支持多异常捕获:

Field field;
try {

field = getClass().getField("testField");
Object value = field.get(this);

} catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}


[b](4)资源释放[/b]
在发生异常时,如果想释放资源,我们不得不先做空判断,然后再关闭,而关闭的时候还有可能继续发生异常:
Reader in = null;

try {
in = new BufferedReader(new FileReader("文件名"));


} catch (IOException e) {

} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


Java 7支持 [url=http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/]try-with-resources[/url]
private static void customBufferStreamCopy(File source, File target) {
try (InputStream fis = new FileInputStream(source);
OutputStream fos = new FileOutputStream(target)){

byte[] buf = new byte[8192];

int i;
while ((i = fis.read(buf)) != -1) {
fos.write(buf, 0, i);
}
}
catch (Exception e) {
e.printStackTrace();
}
}


[b](5)精度计算[/b]
在财务计算的系统中经常会使用到精度计算。Java做大数的精度操作需要用到java.math.BigDecimal和java.text.DecimalFormat。在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。

由于Java没有运算符重载的支持,简单的乘法也不得不写成以下这个样子:
BigDecimal num1 = new BigDecimal("123456.789");
BigDecimal num2 = new BigDecimal("111.111");

BigDecimal result = num1.multiply(num2);


Java中long的最大值是2的63次方减1,如果你想定义一个超过int上限的long
long a = 9223372036854775807; // NG [The literal 9223372036854775807 of type int is out of range]


可以加个“L”:
long a = 9223372036854775807L; // OK


同样的有long-L、double-d、float-f。不知道为什么就不支持大数的精度计算呢。

[b](6)字符串处理[/b]
字符串处理应该是程序中使用最频繁的了,但Java提供的String方法太过于局限,虽然有StringTokenizer但是使用太复杂,所以往往我们不得不使用Commons Lang的StringUtils或者去写自己的StringUtils。
java.lang.String / java.util.StringTokenizer
java.lang.StringBuffer / java.lang.StringBuilder

Apache Commons补充了很多Java的不足,很多开源软件都默认依赖它。[url=http://commons.apache.org/]http://commons.apache.org/[/url]

[b](7)基本类型和包装类[/b]
int/Integer、double/Double等共存,代码中不得不充斥着各种各样的Integer.parseInt()、Integer.valueOf()。而Java5.0开始提供基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing) ,但对于我们经常处理的比如NOT NULL限制的DB字段,很容易发生NullPointerException。

而Map、List、Set集合类中又无法使用基本型,不得不再导入比如:Trove、Google Guava等lib。

[b](8)数组与集合[/b]
比如取个长度,数组用的属性array.length,而集合用的方法list.size()。

[b]泛型:[/b]
public class Test<T> {
public Test() {
List<T> list = new ArrayList<T>(); // OK
T[] array = new T[3]; // NG [Cannot create a generic array of T]
}
}

Scala创始人Martin Odersky说当年正是因为Java泛型的丑陋才创建了Scala。

[b]赋值转换:[/b]
String[] strArray = null;
Object[] objArray = null;
List<String> strList = null;
List<Object> objList = null;

objArray = strArray; // OK
objList = strList; // NG [Type mismatch: cannot convert from List<String> to List<Object>]


[b]集合转数组:[/b]
比如:ArrayList.toArray();
ArrayList<String> stock_list = new ArrayList<String>();
stock_list.add("stock1");
stock_list.add("stock2");

String[] stockArr1 = stock_list.toArray(); // NG [Type mismatch: cannot convert from Object[] to String[]]
String[] stockArr2 = stock_list.toArray(new String[stock_list.size()]); // OK
String[] stockArr3 = stock_list.toArray(new String[0]); // OK
String[] stockArr4 = stock_list.toArray(new String[stock_list.size() + 10]); // OK

需要注意的是stockArr2和stockArr3(重新创建了一个数组返回)能正常返回我们想要的数据,但是stockArr4返回的数组长度为12,除过前两位以外都是null。

[b]集合类不支持基本类型:[/b]
还是需要借助Trove、Google Guava等开源代码

[b](9)日志[/b]
Java诞生后不久log4j就发布了,并且得到了广大程序员的支持,但SUN死活不肯接受log4j 非要自己搞个log框架,自JDK 1.4发布java.util.logging日志包一来直到现在Java 1.7,许多开发者依然使用log4j或者通用的日志工具,而不是使用JDK logger。
[url=http://jcp.org/aboutJava/communityprocess/final/jsr047/index.html]JSR-047(Logging API Specification)[/url]

还有一些正在起草当中的JSR:
JSR 317: Java Persistence 2.0(替代Hibernate)
JSR 107: JCACHE - Java Temporary Caching API(替代EhCache)
JSR 353: JavaTM API for JSON Processing (阿里的温绍锦是该JSR的专家组成员)
......
Java的规范是有名的多而且复杂,有兴趣大家可以看看[url=http://jcp.org/en/jsr/all]http://jcp.org/en/jsr/all[/url],目前都排到JSR 362了。

[b](10)一些易错的API[/b]
比如:
File.mkdir()方法
String.equals()方法
以对象为Key的Map
......

当然,任何语言都不会是完美的,编程语言也都在不断的进化当中。Java中也还有很多相当优秀的API,比如,Doug Lea大师的java.util.concurrent包(JSR 166: Concurrency Utilities),就是功能齐全,性能卓越,优秀完美的并发库。互联网进入大数据处理时代,更加突显这个并发库的重要性。

[size=medium][color=red][b]补充:
1:文中不涉及到类似“泛型”等这种语法糖的问题。只探讨那些常用但是又易错,难用的API。
2:文中不涉及类似Java不支持多继承,没有const关键字等语法上的问题。
3:追加一下Joshua Bloch 的[url=http://dl.iteye.com/topics/download/d5ca9118-f33a-34f7-bc53-2a3b3903017d]《Java Puzzlers: Traps, Pitfalls, and Corner Cases/Java解惑》[/url]希望对大家有所帮助![/b][/color][/size]

参考资料:
[url=http://stackoverflow.com/questions/238173/worst-java-practice-found-in-your-experience]http://stackoverflow.com/questions/238173/worst-java-practice-found-in-your-experience[/url]
[url=http://www.youtube.com/watch?v=hcY8cYfAEwU]http://www.youtube.com/watch?v=hcY8cYfAEwU[/url]
[url=http://d.hatena.ne.jp/ryoasai/20110226/1298703499]http://d.hatena.ne.jp/ryoasai/20110226/1298703499[/url]
[url=http://dev.sei.pku.edu.cn/trac/pkuas/blog/2011/04/06/zhanglei09/my_topic]http://dev.sei.pku.edu.cn/trac/pkuas/blog/2011/04/06/zhanglei09/my_topic[/url]
[url=http://www.cnblogs.com/dushu/archive/2013/01/18/2866379.html]http://www.cnblogs.com/dushu/archive/2013/01/18/2866379.html[/url]
[url=http://www.raychase.net/1098]http://www.raychase.net/1098[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值