關于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中添加的
支持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