重温“ Java Sucks”

总览

关于Java的不足之处(从C开发人员的角度来看)的一个有趣的文档是在一段时间(大约2000年前)写的,但是今天许多论点都像十年前一样真实(或不真实)。
原始的Java Sucks发布。

短消息回顾

Java没有free()。
作者将其列为受益,并且有99%的时间是胜利。 有时候,当您希望进行逃避分析时,没有不利之处 会立即消除,回收或释放您不再需要的对象(恕我直言,JIT / javac应该能够在理论上解决该问题)

词法范围的局部函数
最接近的Java是匿名方法。 对于Closures(Java 8中引入)来说,这是一个差的表亲,但是可以使它做同样的事情。

没有宏系统
您可以使用宏执行许多有用的技巧,Java可以动态地为您执行。 不需要宏系统是一种资产,因为您不需要知道Java何时会为您提供相同的优化。 宏没有应用程序启动成本,并且您无法做真正混淆的事情,但这可能是一件好事。

显式内联函数
JIT可以为您内联方法。 Java可以从共享库内联方法,即使它们是动态更新的。 这确实需要花费运行时间,但是更好的是不必担心此恕我直言。

我发现缺少函数指针是一个巨大的痛苦
函数指针使衬里方法对于编译器更加困难。 如果您使用的是面向对象的编程,那么我认为您不需要这些。 对于其他情况,我相信Java 8中的Closure可能会更好。

静态方法不是真正的类方法的事实是相当愚蠢的
我想大多数Java开发人员都会在某个阶段遇到此问题。 恕我直言:最好的解决方案是将“静态”功能移至其自己的类,并且如果需要多态性则不使用静态方法。

人们如何暗示应该内联一个方法,否则很快就会真正实现,这远非显而易见。
缩小并多次调用。 ;)

两个相同的byte []数组不相等且不散列相同
我同意它的丑陋设计选择不使数组成为合适的对象。 它们继承自Object,但没有toString,equals,hashCode,compareTo的有用实现。 clone()和getClass()是最有用的方法。 您可以改用辅助方法,但是在不同的程序包中有许多不同的辅助类,分别称为Array,Arrays,ArrayUtil,ArrayUtils,这对于新开发人员来说是一团糟。

Hashtable / HashMap确实允许您提供哈希函数
如果您想更改行为,这也是一种痛苦。 恕我直言,最好的解决方案是编写一个实现equals / hashCode的包装器类,但这会增加开销。

迭代字符串中的字符,而不隐式涉及每个字符的六个方法调用
现在有String.toCharArray(),但这会创建您不需要的副本,并且不会被转义分析消除。 如果是这样,这是显而易见的解决方案。 同样的道理也适用于“另一种选择是先将String转换为byte [],然后迭代字节,以创建大量随机垃圾为代价”

在我确定没有非ASCII字符的情况下,Unicode支持会增加开销
Java 6为此提供了-XX:+ UseCompressedStrings解决方案。 不幸的是,Java 7放弃了对该功能的支持。 我不知道为什么要在我做的测试中使用此选项来提高性能(以及减少内存使用)。

接口似乎是一个庞大而俗气的铜版画,可避免多重继承。 他们似乎真的是事后被嫁接了。
我更喜欢只列出所提供功能而不添加实现的合同。 Java 8中更新的虚拟扩展方法将提供无状态的默认实现。 在某些情况下,这将非常有用。

类型提升有些麻烦
Java 5.0+现在支持的协变量返回类型解决了这里的问题。

您不能编写一个期望和对象的函数并给它一个简短的描述
今天,您可以进行自动装箱。 作者抱怨说Short和short不是一回事。 出于效率目的,在某些情况下,使用自动装箱可能几乎没有什么区别。 在某些情况下,它确实有很大的不同,而且我不认为Java在不久的将来会对此进行透明地优化。 :|

如果不知道数组内容的详细信息,就无法遍历数组的内容,这是一种总的痛苦。
很少有您真正需要执行此恕我直言。 您可以使用Array.getLength(array)Array.get(array,n)处理通用数组。 它很丑,但是你可以做到。 它是辅助类之一,它实际上应该是数组本身的方法恕我直言。

处理溢出的唯一方法是使用BigInteger(并重写代码)
诸如Scala之类的语言支持BigInteger的运算符,并且有人建议Java也应如此。 我相信Java 8/9也将考虑溢出检测。

我想念typedef
这使您可以使用基元并仍然获得类型安全性。 恕我直言,真正的问题是JIT无法检测到类型仅仅是原语(或两个)的包装,并且不需要包装的类。 这将提供typedef的优点,而无需更改语法,并使代码更面向对象。

我认为用于模拟枚举和:keywords的可用习语相当la脚
Java 5.0+具有枚举 ,它们是一流的对象,并且功能强大。

没有有效的方法来实现“断言”
assert现在已内置。JIT可以自己实现它。 (大概不是十年前)

通过使“新”成为分配的唯一可能接口,……就有了一整类古老的,众所周知的优化,人们根本无法执行。
这应该由JIT IMHO执行。 不幸的是,它很少这样做,但是这种情况正在改善。

敲定系统很la脚。
大多数人都认为最好避免。 也许它可能更强大,更可靠。 答案可能是ARM(自动资源管理)。

相关地,没有“弱指针”。
Java一直都有弱,软和幻像引用,但是我怀疑这不是这里的意思。 ??

除了内部变量中的最终变量,您什么都不能关闭!
匿名内部类是正确的,但引用字段的嵌套内部类则没有。 封闭可能没有此限制,但可能同样令人困惑。 由于习惯了最终变量的要求,我尤其没有发现这个问题。 因为我的IDE会根据我的要求更正代码。

关于对象的可变性(或只读性)的访问模型受到打击
主要的抱怨似乎是有一些方法可以将最终字段视为可变的。 这是反序列化和依赖注入程序所必需的。 只要您意识到自己有两个可能的行为,一个级别比另一个级别低,它就会比问题更有用。

该语言还应规定字面常量是不变的。
文字常量是不可变的。 看来作者想扩展什么是文字常量。 恕我直言,以C ++的方式支持const很有用。 const是Java中的关键字,而无需创建多个实现或只读包装器来定义类的不可变版本的功能将更有效率。

锁定模型已损坏。
锁定问题的内存开销实际上是一个实现细节。 由JVM决定标头的大小以及是否可以锁定标头。 另一个问题是无法控制谁可以获取锁。 解决此问题的常用方法是封装您的锁,这是您在任何情况下都必须要做的。 从理论上讲,锁可以被优化掉。 当前,只有在优化整个对象时才会发生这种情况。

没有抛出就没有信号
为此,我将侦听器模式与onError方法一起使用。 语言对此没有支持,但我认为没有必要。

应该将foo.x定义为等同于foo.x(),
也许foo.x => foo.getX()会是更好的选择,就像C#一样。

编译器应该能够轻松地内联零参数访问器方法,以内联对象+偏移量加载。

JIT这样做,而不是编译器。 这样就可以在编译被调用方之后更改调用代码。

方法“属于”类的概念是la脚的。
这是某些语言支持的“酷”功能。 在更动态的环境中,这看起来更好。 不利的一面是,您可以在整个地方为某个类编写一段代码,并且您将不得不采用某种方式来管理不同库中的重复项。 例如,库A定义了一个新的printString()方法,库B也为同一类定义了一个printString方法。 您将需要使每个库看到其自己的副本,并具有某种方法来确定C调用此方法时需要哪个版本的库。

图书馆

它带有哈希表,但没有qsort
它带有一个“优化的合并排序”,旨在加快速度。

字符串的长度为+ byte []的开销+24个字节
也就是说,无需考虑两个对象中的每个对象都与8字节边界对齐(使其更高)。 如果听起来很糟糕,请考虑将malloc对齐16字节,最小大小为32字节。 如果将shared_ptr用作byte [](以提供类似的资源管理),则在C ++中,它可能比Java大得多。

造成这种开销的唯一原因是String.substring()可以返回共享相同值数组的字符串。

这是不正确的。 问题是Java不支持可变大小的对象(数组除外)。 这意味着String对象是固定大小的,要拥有可变大小的字段,您必须拥有另一个对象。 无论哪种方式都不是很好。 ;)

String.substring可能是“内存泄漏”的来源
您必须知道要对您进行显式复制,才能保留较大字符串的子字符串。 这很丑陋,但是好处通常超过了缺点。 更好的解决方案是能够优化代码,以便默认情况下采用防御性副本,除非不需要防御性副本(已将其优化掉)

文件操作原语不足
Java 7中改进了文件系统信息。我认为这些选项不可用,但是如果您需要知道这些选项,则可以很容易地推断出它们。

这不是问“我在Windows上运行”还是“我在Unix上运行”的可靠方法。
系统属性os.name,os.arch,os.version一直存在。

在Unix上无法访问link(),这是实现文件锁定的唯一可靠方法。
这是在Java 7 创建硬链接中添加的

除了复制并重命名整个文件外,没有其他方法可以执行ftruncate()。
您可以使用RandomAccessFile.truncate()。 在Java 1.4中添加。

“%10s%03d”真的有太多要求吗?
它是在Java 5.0中添加的

RandomAccessFile不能用作FileInputStream或FileOutputStreamRandomAccessFile

支持DataInput和DataOutput,FileInputStream和FileOutputStream可以包装在DataInputStream和DataOutputStream中。 可以使它们支持相同的接口。 我从未遇到过要在单个方法中同时使用两个类的情况。

markSupported是愚蠢的
真正。 有许多愚蠢的方法只是出于历史目的。 另一个是在每个对象(甚至数组)上的Object.wait(millis,nanos ),但是nanos从未真正使用过。

世界和系统运行时之间有什么区别?
我同意这似乎是任意的,在某些情况下会增加一倍。 System.gc()实际上调用Runtime.getRuntime()。gc(),但即使在内部代码中也被称为System GC。 在后站点中,它们实际上应该是一类,并将监视功能移至JMX。

世界上是在基础语言类库中进行的像checkPrintJobAccess()这样的应用程序级废话
因此,您的SecurityManager可以控制是否可以执行打印。 (也不必具有应用程序级安全性管理器)不确定是否确实阻止了对应用程序级安全性的需求。 ;)

参考:Java Java博客上,我们的JCG合作伙伴 Peter Lawrey 重新审视“ Java Sucks”


翻译自: https://www.javacodegeeks.com/2012/01/java-sucks-revisited.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值