Java语言自从1995年正式推出以来,已经成为被广泛采用的编程语言之一。
《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的特性列表中,期待有所改善吧!
[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支持
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
[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。
还有一些正在起草当中的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]