`
rensanning
  • 浏览: 3552865 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:38220
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:607621
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:682811
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:89542
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:402154
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69762
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:91840
社区版块
存档分类
最新评论

那些丑陋的Java API

    博客分类:
  • Java
 
阅读更多
Java语言自从1995年正式推出以来,已经成为被广泛采用的编程语言之一。在TIOBE编程语言排行榜榜单上也一直保持着前几名的位置。

《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”吧!

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

(2)日期处理
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的特性列表中,期待有所改善吧!

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

(3)异常捕获
比如以下一个简单的反射获取字段值,需要捕获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。

XStream 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();
}


(4)资源释放
在发生异常时,如果想释放资源,我们不得不先做空判断,然后再关闭,而关闭的时候还有可能继续发生异常:
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支持 try-with-resources
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();
    }
}


(5)精度计算
在财务计算的系统中经常会使用到精度计算。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。不知道为什么就不支持大数的精度计算呢。

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

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

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

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

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

泛型:
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。

赋值转换:
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>]


集合转数组:
比如: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。

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

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

还有一些正在起草当中的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的规范是有名的多而且复杂,有兴趣大家可以看看http://jcp.org/en/jsr/all,目前都排到JSR 362了。

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

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

补充:
1:文中不涉及到类似“泛型”等这种语法糖的问题。只探讨那些常用但是又易错,难用的API。
2:文中不涉及类似Java不支持多继承,没有const关键字等语法上的问题。
3:追加一下Joshua Bloch 的《Java Puzzlers: Traps, Pitfalls, and Corner Cases/Java解惑》希望对大家有所帮助!


参考资料:
http://stackoverflow.com/questions/238173/worst-java-practice-found-in-your-experience
http://www.youtube.com/watch?v=hcY8cYfAEwU
http://d.hatena.ne.jp/ryoasai/20110226/1298703499
http://dev.sei.pku.edu.cn/trac/pkuas/blog/2011/04/06/zhanglei09/my_topic
http://www.cnblogs.com/dushu/archive/2013/01/18/2866379.html
http://www.raychase.net/1098
25
6
分享到:
评论
22 楼 ansjsun 2013-04-09  
回复:alvin198761

alvin198761 写道
......


职业喷子..3年java气急败坏....

1.你用jdk自带的和 dom4j 写起来比比就知道了.

2.数组那个其实jdk7已经处理了..那个问题很困惑程序员...Object[] 无法转型..你要没遇到过.只能说你代码写的不够

3.tonker String类..的确用的人不多..其实java挺强大的..你认为string只有正则么?

5.io关流代码的确不优雅..jdk7 已经处理了.. 至于nio ...其实 jdk的io已经用nio重写了..建议你看看jdk源码去


6.注意下论坛秩序.楼主提出的这些问题的确是jdk中存在的..有的已经修复..有的还有..比如日期类一直让人不爽.

还有 alvin198761  你可以多看看其他语言..做比较...比如python 对io 文件读写的方式...

也可以看看java 的guava包...  希望你能有点收获...



21 楼 alvin198761 2013-04-09  
(9)日志
Java诞生后不久log4j就发布了,并且得到了广大程序员的支持,但SUN死活不肯接受log4j 非要自己搞个log框架,自JDK 1.4发布java.util.logging日志包一来直到现在Java 1.7,许多开发者依然使用log4j或者通用的日志工具,而不是使用JDK logger。
-----------
我们公司用的是jdk自带的,很好用
我在说个例子,java自带有解析xml的,但是很多程序员非要用dom4j.jar,不是jdk自带的不行,而是国内一些人看不懂api,baidu出来的例子是什么,就用什么,
---------------
不想说你了,
20 楼 alvin198761 2013-04-09  
(8)数组与集合
比如取个长度,数组用的属性array.length,而集合用的方法list.size()。

泛型:

Java代码 
1.public class Test<T> {  
2.    public Test() {  
3.        List<T> list = new ArrayList<T>(); // OK  
4.        T[] array = new T[3]; // NG [Cannot create a generic array of T]  
5.    }  
6.}
------------------
数组说长度,
集合说大小,
不对吗?是你英文差吧!
Test<T>的代码是用来干嘛的?那个业务说说看?说到业务了,你看看这个代码应不应该是这样呢,什么是解耦合??这样的代码就能解耦合
19 楼 alvin198761 2013-04-09  
字符串处理应该是程序中使用最频繁的了,但Java提供的String方法太过于局限,虽然有StringTokenizer但是使用太复杂,所以往往我们不得不使用Commons Lang的StringUtils或者去写自己的StringUtils。
java.lang.String / java.util.StringTokenizer
java.lang.StringBuffer / java.lang.StringBuilder
---------------
连正则都不懂的人,就别说String不行,是自己不行吧
18 楼 alvin198761 2013-04-09  
Java中long的最大值是2的63次方减1,如果你想定义一个超过int上限的long

Java代码 
1.long a = 9223372036854775807; // NG [The literal 9223372036854775807 of type int is out of range] 
long a = 9223372036854775807; // NG [The literal 9223372036854775807 of type int is out of range]

可以加个“L”:

Java代码 
1.long a = 9223372036854775807L; // OK 
----------------------
int的长度本来就是有限制的,别的语言没有?
加个“L”是编程规范吧,很多语言都是可以,不是吗??
17 楼 alvin198761 2013-04-09  
1.// Return Last Day of a Month "2013/4/8"  
2.Calendar calendar = Calendar.getInstance();  
3.calendar.set(Calendar.YEAR, 2013);  
4.calendar.set(Calendar.MONTH, 4 - 1);  
5.calendar.set(Calendar.DATE,;  
6.calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));  
7.// calendar.add(Calendar.DATE, -1);  
8. 
9.Date dd = cal.getTime();  
10. 
11.SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");  
12.String lastDay = sdf.format(dd);
----------------
获得2013/4 用add -1就好了,你自己搞的这么复杂
16 楼 alvin198761 2013-04-09  
怎么就不那个更好的语言的比较一下?
从上面来看,估计你就是一个做WEB开发的,
我拿几个例子来说:
1.Field field;  
2.try {  
3.      
4.    field = getClass().getField("testField");  
5.    Object value = field.get(this);  
6.      
7.} catch (SecurityException e) {  
8.    e.printStackTrace();  
9.} catch (NoSuchFieldException e) {  
10.    e.printStackTrace();  
11.} catch (IllegalArgumentException e) {  
12.    e.printStackTrace();  
13.} catch (IllegalAccessException e) {  
14.    e.printStackTrace();  
15.}
每个一次都可以单独处理,如果你不处理,或者想同意处理:
try{
//code 就可以了
}catch(Exception e){

}
----------------------
这下面的代码是jdk1.几?知道是那一年的吗?都用nio了楼主,你估计连java有nio的包你都不知道吧!
1.private static void customBufferStreamCopy(File source, File target) {  
2.    try (InputStream fis = new FileInputStream(source);  
3.        OutputStream fos = new FileOutputStream(target)){  
4.         
5.        byte[] buf = new byte[8192];  
6. 
7.        int i;  
8.        while ((i = fis.read(buf)) != -1) {  
9.            fos.write(buf, 0, i);  
10.        }  
11.    }  
12.    catch (Exception e) {  
13.        e.printStackTrace();  
14.    }  
15.} 
-----
File.mkdir()这个容易出错,是因为你没有看API,他创建的是一层目录
File.mkdirs()才是创建多个目录的,你骂API,你看过API吗,你看了它上面的注释了吗?
String.equals()这个哪里容易错?举个例子给我看看
---
Scala创始人Martin Odersky说当年正是因为Java泛型的丑陋才创建了Scala
他除了说别人不行以外,还有什么本事,他超越了java?搞不过别人就把一个小的问题放大罢了。
----
最后说一句,楼主你牛,你倒是做个更好的让我开开眼界啊
15 楼 十三月的 2013-04-09  
这么一说确实觉得不是很舒服.
14 楼 windshome 2013-04-09  
说出了Java开发者的心声,呵呵,我也来给补充一点:
第(4)资源释放问题是C++和Java都有的问题
第(9)java的日志API在并发服务器端还有性能问题,能烦死,呵呵;而log4j要好很多,我唯一觉得log4j的一点不爽就是常见的那种写法,在类中这样定义:

    static Logger logger = Logger.getLogger(LogDemo.class);

我觉得这样的代码怪怪的,代码应该描述业务。

我经历的几个问题:
(1)Socket问题 请参看 [url] http://windshome.iteye.com/blog/1836885[/url]

(2)线程池问题  http://windshome.iteye.com/admin/blogs/1811225


13 楼 rensanning 2013-04-09  
jorneyR 写道
"丑陋的Java API"这个标题有哗众取宠之疑,叫做“Java中丑陋的API”比较合适。

嗯,确实有点儿标题党。正如文中所说“任何语言都不会是完美的,编程语言也都在不断的进化当中”。
12 楼 jorneyR 2013-04-08  
"丑陋的Java API"这个标题有哗众取宠之疑,叫做“Java中丑陋的API”比较合适。
11 楼 itlangqun 2013-04-08  
有些说的对,有些不完全对。说到字符串你看看C/C++,各个框架各有一套,各家又各自一套API,不弄死人也算个奇迹。简单即美,这个是我看待这个问题的考虑出发点。
10 楼 fxhbeyond 2013-04-08  
m  ark
9 楼 sgp420 2013-04-08  
总结得超好。。顶
8 楼 小林信仁 2013-04-08  
体系庞大了没办法,各取所需。不一定都用得到啊!
7 楼 sswh 2013-04-08  

不过因为这些丑陋的API  只占我们平时使用概率的10%  ,其他90%的API是易用的,优秀的。

正是这个原因,JAVA才被众多人所喜爱。

所谓人无完人。
6 楼 damoqiongqiu 2013-04-08  
漏掉了<泛型>这块鸡肋
5 楼 ansjsun 2013-04-08  
这些砖基本都拍到点上了
4 楼 netatomy 2013-04-08  
好文,看后有收获。
3 楼 MrLee23 2013-04-08  
就因为这些所谓的缺点,所以我们爱java,就因为这些所谓的缺点,我们更了解了java更了解了编程。萝卜白菜,各有所爱。

相关推荐

    working-gym-java-client:一个Java客户端来访问OpenAI Gym HTTP服务器

    OPEN AI GYM HTTP服务器的JAVA CLIENT 一个访问简单Java客户端。...去做: 现在,一些api调用返回丑陋的东西,例如。 resetEnv返回一个可能是JSONArray的Object 。 这是因为不同的环境可能返回不同的结

    Hibernate 3.3 中文API 帮助文档

    《Hibernate 3.3 中文API 帮助文档》是针对Java开发者的一份详尽资源,尤其适合Hibernate框架的初学者。Hibernate是一个开源的对象关系映射(ORM)框架,它允许开发人员使用面向对象的编程模型来操作数据库,而无需...

    java版贪吃蛇

    6. **图形绘制**:在Java中,可以使用`Graphics2D` API来绘制游戏元素。`GameBoard`类需要根据蛇和食物的位置在画布上画出它们的形状,同时更新背景。 7. **状态管理**:游戏需要记录分数、生命值等状态,当蛇吃到...

    app_java_compiler:Android APP,用于编辑和运行Java代码

    online compile(API已经暴露) compile 非常慢 缺点:界面丑陋 no G11n Java编译器 支持写代码,保存,加载文件 图标做的比较漂亮 常用符号快速输入(Tab, {}, ;, \n) 缺点:执行结果是用dialog弹出,不美观 G11n ...

    API-CRUD:API com Knex.js

    带Java脚本SQL查询生成器 回调样式或Promise样式 多种平台(PostgreSQL,MySQL,SQLite3 ...) 敏捷 在所有情况下都有帮助吗? 不,但是您仍然可以编写原始查询 如果不仔细操作,SQL Raw可能很危险,可能很丑陋,...

    JAVA Dynamic SQL Assistant-开源

    "JAVA Dynamic SQL Assistant"通过提供一种面向对象(OO)的方法,让开发者能够避免这种“丑陋的字符串连接代码”,从而提升代码的可读性和可维护性。 描述中提到,该软件包可以被编译为COM DLL,这意味着它可以在...

    phpcr:Java内容存储库(JCR)到PHP的端口

    规范定义了内容存储库(CR)的... 不再学习(数十个)(丑陋的)专有API 编写(主要是)可移植的代码,用于文档管理,Web内容管理,源代码控制比较存储库功能没有更多信息孤岛和供应商锁定以内容为中心的基础架构PHPCR

    java二八杠源码-test:测试

    java二八杠源码格拉菲卡 欢迎来到 Grafika,这是一个 Android 图形和媒体黑客的垃圾场。 涂鸦是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧版本的 ...

    java二八杠源码-recordgl:记录gl

    java二八杠源码格拉菲卡 欢迎来到 Grafika,这是一个 Android 图形和媒体黑客的垃圾场。 涂鸦是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧版本的 ...

    JavaFX手机编程

    - **集成Java 2D API**:JavaFX不仅包含了传统的Swing组件,还提供了类似SVG的接口来访问Java 2D API,这样就可以直接使用这些API来创建更为复杂和美观的界面元素。 - **声明式表达**:通过声明式的语法,开发者可以...

    java二八杠源码-android---grafika:android---图形

    java二八杠源码格拉菲卡 欢迎来到 Grafika,这是一个 Android 图形和媒体黑客的垃圾场。 涂鸦是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧版本的 ...

    profique:我当前的 GAE Java 堆栈

    我最喜欢的 Java Google AppEngine Stack,用于基于 REST 的服务器后端。 Objectify - 用于数据访问 Jersey - 用于 REST API,包括文件上传 Guice - DI 将这一切结合在一起 gson - 用于 JSON 序列化 hibernate-...

    GaiaLib:Java LibGDX框架的C Sharp实现(正在开发)

    开发过程中,"重组/重写IntArray()-非常丑陋"这部分可能是指在将Java的数组类型转换为C#的过程中遇到的挑战。在Java中,`IntArray`可能是自定义的类或者LibGDX提供的特定数据结构,而在C#中,可能需要使用类似`...

    java二八杠源码-gles:格莱斯

    java二八杠源码格拉菲卡 欢迎来到 Grafika,这是一个 Android 图形和媒体黑客的垃圾场。 涂鸦是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧版本的 ...

    java二八杠源码-grafika:图形

    java二八杠源码格拉菲卡 欢迎来到 Grafika,这是一个 Android 图形和媒体黑客的垃圾场。 涂鸦是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧版本的 ...

    java二八杠源码-VideoHacks:视频黑客

    java二八杠源码视频黑客 欢迎来到 VideoHacks,这是一个 Android 图形和媒体黑客的垃圾场。 VideoHacks 是: 一系列使用图形功能的黑客。 为 API 18 (Android 4.3) 开发的 SDK 应用程序。 虽然某些代码可能适用于旧...

Global site tag (gtag.js) - Google Analytics