臭名昭著的Java错误和陷阱

在2000年,我上大学,濒临选择一种语言来发展自己的职业。 Java尚未成为主流,但很受人们欢迎。 与静态html页面相比,小应用程序(尚未破碎)花哨且富有光泽。 Swing不是构建桌面应用程序的不错选择。 J2EE越来越受到关注。 从那时起已经过去了13年,尽管小程序失败了,Java成为主流,尽管对于小应用程序而言,它并没有真正考虑到这一点,而且J2EE太复杂了,甚至无法构建简单的东西,但是仍然没有什么阻止Java成为最受欢迎的编程语言。

毫不奇怪,Java非常漂亮,类型安全且易于学习。 Java中有很多很好的实现细节,例如垃圾收集器,字符串
(最终类),提供了合并和快速排序的出色实现的集合,内置在哈希码方法中的等等。 但是,Java仍然远非完美,可能会引入一些意想不到的行为。

Abs错误:

好吧,这是一个非常小的缺陷,但是Math.abs()函数可能会返回负值。 奇怪的? 实际上很简单,Java整数可以得到-2,147,483,648到2,147,483,647之间的值,这清楚地表明-2,147,483,648不能以正数表示。

那是一个错误吗? 好吧,期望值是正的,所以可以肯定,但是最终这实际上是一个溢出。 那么如何解决呢? 一种方法是在使用abs函数或使用位运算符代替之前操纵Integer.MIN_VALUE。

自动装箱基元陷阱:

通过自动装箱,可以轻松地使用基本类型及其对象对应物。 但是,在它们之间移动可能会导致某些意外行为。 例如,不能使用==运算符将Integer i1 = 6与Integer i2 = 6进行比较,其中可以将int i3 = 6与具有==的整数进行比较。 但是,使用equals可能也无法按预期工作。 例如Long x = 0L; 当x.equals(0L)返回true,而当x.equals(0)返回false。 奇怪的? 并非如此,因为x很长,其中0(没有L)是int。 因此,它们甚至不是相同的对象类型。 还将原始类型与集合一起使用可能会导致意外行为。 最后,自动装箱可能会导致过载问题。 假设我们有Integer i = 6并调用方法sum(i); 我们有两种求和方法,例如; sum(long val)和sum(Long val)。 您认为会叫哪一个? 还是合理的,但不希望乍一看,可能会导致应用程序出现问题。

BigDecimal构造函数错误:

如果您尚未查看Joshua Bloch的Java Puzzlers 。 如果使用双精度构造函数创建两个大十进制数(x1 = new BigDecimal(2.00)和x2 = new BigDecimal(1.10)),然后使用减法(x1.subtract(x2)),则最终结果为0.8999999999。 BigDecimal的double构造函数无法按预期工作,而需要使用字符串构造函数(新的BigDecimal(“ 2.00”))。 这可能是一个严重的问题,因为BigDecimal被广泛用于金钱计算!

System.out.println陷阱:

println()是对CS学生更难的第一个函数。 它很容易并且经常使用。 当您尝试某些逻辑或调试某些值时,通常可以使用。 但是System.out是同步的,因此在访问时会获取一个锁。 因此,使用println可能会使您的应用程序在同步上下文中运行,这实际上意味着访问println时线程将被阻塞。 想象一下使用println的Web服务器和应用程序日志记录,您最终将获得线程锁,并且每个请求都在等待其他请求。 因此,println可以而且有用,但不适用于实际的应用程序和日志记录!

地图错误:

再次看一下Joshua Bloch的Java Puzzlers ,第五个难题(大小很重要)在HashMap和EnumMap之间引入了一种奇怪的行为,其中具有相同的值,一个地图的大小为2,另一个地图的大小为1。作为IdentityHashMap,EnumMap可能会引入此行为。

这是错误吗? 可以肯定的是,我们期望地图实现中具有相同的原理,但是Bloch描述了这一点,因为当时的规范尚不清楚。

CPU编号错误:

除非您真的依赖硬件,否则这可能不是一个大问题。 为了获得可用处理器数,Java提供了Runtime.getRuntime()。availableProcessors()方法,该方法返回一个int数作为可用处理器数。 但是,如果尝试一下,最终可能会得到意外的数字。 例如,在我的四核i7上,我得到8。因此,此方法不返回硬件cpus数量,也不是核心数量,而是返回执行引擎(虚拟核心)数量。 就我而言,因为四核i7支持超踩,所以实际上它就像拥有八核。

那是一个错误吗? 绝对不是,因为硬件和操作系统的运行方式就好像它们具有的物理CPU数量一样,但是如果您依靠可靠的硬件,仍然要小心。

通用数组

在Java中,创建数组的方式如下:int [] arr = new int [5]; 因此,如果您具有T的通用类型,则希望以这种方式创建通用数组:T [] = new T [5]; 但根本不能。 Java不允许创建泛型数组,这实际上是因为泛型是使用Erasure在Java中实现的。 泛型仅在编译器级别实现,实际上每个类仅生成一个类文件。 因此,要创建数组,我们需要进行如下丑陋的转换:T [] =(T [])new Object [5]; 当您尝试编译时,编译器会发出警告,提示您正在进行不安全的转换!

当然,这不是错误,它只是在实现泛型时为了简化和兼容性而给出的实现问题。 但是在设计问题上发出编译器警告可能会使第一次遇到该问题的人感到困惑。 因此,这绝对不是清单的结尾,但是Java仍然提供了漂亮的语法,类型安全性和易于实现的易于学习的语言。 最后,没有语言或实现是完美的!

参考: Developer Chronicles博客上来自我们JCG合作伙伴 Murat Yener的臭名昭著的Java错误和陷阱

翻译自: https://www.javacodegeeks.com/2013/04/infamous-java-bugs-and-pitfalls.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值