java 析构函数_C++与Java的区别(一)
一,前言
網上經常看到編程語言之爭,大伙兒皈依到不同門派,各自懷抱信仰,時不時還發生點兒“沖突”。
這其中,C++和Java的優劣,十多年前就常吵的火熱。然而時代在進步,技術在發展,滿街早已是“云大智”了,現在還討論C++ vs Java這個話題,似乎早已經過時了。
家鴿資質魯鈍學的慢,熱門的知識沒掌握,只能拿這個老話題炒炒冷飯。
參加工作的前兩年,主要寫Java;近些年由于工作需要,主要寫C/C++了。也算是敝帚自珍吧,常覺得之前學會兒點兒東西不容易,舍不得丟掉。總會自覺不自覺的拿現在用的相互對比,也就粗略的總結了些東西,又怕記性不好以后忘了,就寫個文字形式的記下來。
這個系列不敢說兩種語言孰優孰劣,只妄圖達成一個具體的目標:認真看過這些文字的人,以前寫主寫C/C++的,也能夠寫Java了;以前主做Java的,也可以嘗試做C/C++了。這當然僅限于語言本身這個層面,框架與應用領域等范疇不在此列。與此同時,要是家鴿能夠略微傳達出一些觀念,那就更好了。
二,形而上形而上謂之道。人在道中而不知道,如同魚在水中卻不知水一樣。
本節以下為家鴿自己看法的總結,純屬一家之言,有不同意見歡迎拍磚。語言層面的“道”,是它的設計理念與發展思路。先看一下C++。它就是一個實用主義的大雜燴,有點兒吸星大法的感覺。
1979,Bjarne等人試圖去分析UNIX的內核的時候,誕生了這門語言。它初期的目標,首先是在兼容C的前提下,引入“面向對象”思想。1984年,“C with class”才改名為“C++”。當時面向對象思想剛出來沒多久,炒的火熱,也確實能解決很多問題。“兼容C”這個大前提不光是兼容,顯然是當時出于復用與實用性的考慮。導致它近乎完全吸納了C的風格,可以面向過程,又能面向對象(這個對象顯然不純,不如稱之為基于對象),于是語言特性和寫法不可避免的增加了不少,相對而言顯得有些復雜。面向對象是C++編程思想的一個重要的部分,但不是它最核心的思想,從來也不是。STL也于1979年創立,于1993年成為C++標準的一部分。泛型思維是STL的基礎。當前的STL就是個以高度復用為目標的,以泛型思維為基礎的,高層次的,系統化的,類別分明的C++庫。早期的C++并不支持template,而是用宏來構建復雜的結構。但很快就引入了template,又有了新的風格與寫法。如果去分析不同版本的STL源碼,會發現其中沒多少面向對象,都是在泛型思維指導下,使用template實現的具有高復用性低時間復雜度的精巧代碼。早期的Java沒有泛型,后來也有了(雖然二者機制與地位并不等同)。可以看到語言之間常常會相互學習各自的語言特性。性能的考量以及直接而靈活的資源操控與系統調用,是這門語言設計的又一目標。它本來就為分析與實現操作系統而誕生的語言,操作系統內核里不會講什么面向對象,主要是各種資源操控與管理的策略及算法。在從始至終的發展過程中,所有新特性的引入,都不會違背這個性能與資源操控這個前提。由于這個原因,這門語言靈活而強大。有這么一個說法,C++的靈活與強大不在于它能實現多高層的抽象,而恰恰在于它可以不抽象,以及可以自如的控制自己抽象的程度,要是覺得C/C++語言本身這一層抽象也礙事,甚至可以在里面嵌入匯編。在C++的世界里,既可以面向實際問題的結構,建立抽象模型,又可以看到解決問題時基于的計算機的結構,實際項目研發過程中一個重要的設計,就是建立二者之間的映射關系,這個過程需要不菲的成本。在更高層面抽象的語言誕生之后(比如Java),依項目類型和需求而定,這種映射關系的充分建立也許就不是必要的了。也是由于這些原因,使得C++不容易掌握,更難以精通,不易維護,開發效率低下,容易引入嚴重的內存泄漏和宕機等問題。于是實際工作中也就有了各種版本的開發規范。11版本及之后的C++吸取了現代語言的新思想,譬如有人說lamda和std::function std::bind讓它能夠進行函數式編程,能支持高階函數和鏈式調用。也許在正統函數式編程語言的支持者看來,這個“函數式編程”未必純正。但對新思想的吸納,給C++帶來很多新的語言特性。活用種種新特性與新風格寫的代碼,看起來似乎成了一種新的編程語言。C++委員會一方面想讓這門語言長盛不衰,給開發者提供更加便捷,安全的語言特性;另一方面必須保持它在已有領域的優勢,能夠相對靈活自由的操作底層資源;當然必須保持對歷史代碼的兼容,以前的特性不能丟。這中間免不了權衡取舍綜合折中,就成了我們現在看到的C++。這種權衡與引入,形成了當前一個重要的趨勢:在提高(至少保持)對資源靈活控制能力的前提下,提高抽象程度,提升編程的方便與程序安全性。但在語言快速發展的過程中,對舊特性老式風格的兼容,legacy代碼的維護,大部分一知半解的程序員,以及各種編程范式與新老風格在實際研發中的無原則的混用,都如同重重迷霧一般,掩蓋了這種趨勢,C++項目看起來反而愈加復雜,甚至有些讓人望而生畏。總體上,C++是面向過程、基于對象、泛型思維和新思想并重,還在不斷發展的一門編程語言。Java的設計者看到了(當時)C++的各種問題,總結實際項目的研發需求,形成了一套高度抽象與精煉的語言特性體系集合,從研發的源頭就規避掉這些問題(與此同時也限制了自身在底層的能力)。這個精煉集合寫出來的代碼未必精煉了,有不少人評價它的語言風格有些啰嗦,但相對而言這種問題就無傷大雅了。Java的語言特性相當精煉,容易掌握,以至于很大一部分Java程序員相對的不怎么討論語言特性本身,能更多的面向實際問題,將主要精力放在各種框架,API,設計模式中。你當然可以說,C++項目那些問題是由于編程人員水平不夠,而不是語言本身的問題。但在Java這一套新的語言體系中,更多的人經過短期學習,更容易高效的寫出問題很少的代碼,何樂而不為呢?
高度抽象,是Java的設計原則。Java本身就是抽象而完備的;并且基于它可以進行更高層面的抽象。這使得代碼量少,維護性好,研發工作高效,安全性容易得到保證。純粹的面向對象是Java語言核心的思想與原則,在Java的王國里“一切都是對象”,連基本類型都有對應的包裝類。它的復用也是更多基于面向對象來實現的。更完備的抽象,完全的面向對象,意味著更完全的封裝,程序員不再需要知道細節就可以完成任務,也可以把無關他人的細節更好的隱藏起來。當然在一些情況下,掌控底層成為一種必要,它就無能為力了。在Java的設計思想中,開發效率的重要性高于程序運行效率本身,這在計算資源不是主要約束的實現需求中,顯然有很大優勢。
質量與安全性也是重要的考慮,除了規避掉容易引入問題的語言特性,Java風格中的異常處理機制非常完善。很多時候同一段邏輯,Java的寫法也相對單一,不夠自如靈活,但安全性與質量更高。在自由與安全中,Java選擇了安全。
Java的語言特性相對精煉,但它也有C++不具備的語言特性。這些語言特性或者更加面向對象,比如接口。或者使它更能適用于Web,并發(C++也引入了)等領域當中。本節的描述有些抽象,不易理解,至少不容易有切身體會。為了說明問題,后文中家鴿會用自己寫的一些代碼示例,C++代碼用黑背景,Java用淺綠。這些示例僅為突出演示某些語言特性的“玩具例子”,盡可能的簡短易懂,但并不適用于實際工程。三、 形而下
形而上謂之道,形而下謂之器。舍器而近道者,幾稀!器,可以簡單的理解為工具。編程語言本來就是工具,工具的區別在于使用方法。上圖中,C++,C,Pascal都類似瑞士軍刀,是用來做細活的工具。C語言的刀上有個USB,說明是可以做硬件操作的。C++刀是什么都有,說明C++是一種特性(過分)繁多的語言。Python是把電鋸,面對大型的物體的修整,威力很大,人擋殺人,佛招殺佛,比C++/C/Java什么的得心應手得多得多,但是相對并不適合一些精細的調優工作。Java/C#是一把單刃工具刀,相對來說,其語法和使用相并不復雜。
不太關注技術細節的朋友,這一節后面的文字可以略過,直接看下一節。
這一節從語言具體的用法出發,來分析一下C++與Java二者的區別。可以粗分為三類:
Java不支持的C++特性
C++不支持的Java特性
C++和Java都有但是卻不相同的特性
限于篇幅,本文只闡述前兩種情況,第三種情況后續系列中再行闡述。
Java不支持的C++的特性Java不支持指針;
Java不支持預處理,也不再支持預處理指令(宏等等都沒有了);
Java不支持typedef;
Java不支持goto語句;
Java不支持結構體與聯合體;
Java不支持操作符重載(比如Java中的<>不再重載I/O操作);
Java不支持全局變量或全局函數;
Java不支持默認參數。
Java不支持析構函數。(Java增加了finalize()函數);
Java不支持delete操作符;
Java中,參數傳遞的方式只能是傳值(“傳值”這個說法有爭議,且看后文詳細解釋。C++中可以傳值、傳指針或傳引用);
Java不支持多重繼承,即不允許一個子類繼承多個父類。
............
下面挑幾條典型的詳細分析一下。
指針
最顯然的區別是,C++支持指針,而Java不支持。
下面這個例子,將平面一個點,移動到與它原點對稱的位置。
上面例子的寫法都有些笨拙。比如可以用STL的copy()函數也許會清爽一些。這里只為比較"指針"這一點。
可以看到,C++有了指針,靈活的多也自由的多,在細節操作方面有更多掌控力;但代碼也復雜了。比如*p++這種寫法,不熟悉的話,總會糾結優先級結合方式之類。指針操作不當也有相當的風險,加之可讀性下降,一般說來開發效率是有所降低的。
Java在設計之初,就把安全性與開發效率作為考慮的重要因素。在語法上去除指針,從根本上就避免了一些可能的質量問題。而且語法相對簡單,容易上手,不用太過小心翼翼規避問題,開發效率相對也會高些。
對象的生命周期? ?
標號為(1)的C++構造函數調用中,我們可以把u本身當作一個User對象。實際是在棧空間中構造的對象。這種構造方法Java中并不支持(基本類型除外)。標號為(2)的C++構造函數調用中,使用一個指針p指向堆空間中構造的對象。類似Java中引用q的作用。
也許在C++程序員看來,這沒什么區別,只是悄悄的把指針藏起來了。換成了“引用”這一語法糖,底層還是用指針實現的。事實上,Java的引用與C++的引用有重要的不同(具體下一段中分析),“引用的底層也是指針”這一說法,也僅僅是對內部實現的一種假設模型,對理解某些問題更加方便,但把它理解為“別名”更為恰當。至于析構函數與finalize()函數,它們在機制上有著本質的區別。下面這個例子中,假設資源有限,該類型對象只能創建有限個,需要計數控制。C++棧空間的對象會及時析構并釋放空間(堆空間的對象則可在主動delete時釋放),Java的finalize()只能在系統進行自動垃圾回收時調用。程序運行時報錯的原因是每次f()返回時垃圾回收并不出現。因此,沒有調用finalize()方法,count的值也沒有減少。在5次調用方法后,count到達其最大值。C++有析構函數,可以自主決定堆中對象的析構時機。Java沒有這個機制。析構的時機不能自主掌控,代碼優化空間相對有限。因而內存占用率等指標無法與精心優化過的C++代碼相比較,性能也會多少受影響。視具體軟硬件運行情況,垃圾回收時可能會出現若干毫秒的延遲,不能適用于一些資源有限或高度實時的應用場景。但Java寫法簡單,容易掌握。而且不容易出錯。C++中忘記析構導致內存泄露,重復析構引發崩潰等問題,Java中極少遇到。讓C++程序員飽受折磨的內存問題,在JAVA的GC機制下不再是什么問題了。質量與安全性有了保證。參數傳遞
家鴿喜歡的所謂“Java一切皆傳值”的說法,對于基本數據類型,指的便是變量值的拷貝,而對于對象(或String、Integer等包裝基本類型),指的是對象地址的拷貝,可以理解為所傳遞的值是對象的地址。
C++的參數傳遞有傳值,傳引用,傳指針的用法,還可以傳多重指針,搭配上const和參數的析構問題,相對復雜很多;C++程序員始終面臨著什么情況下什么樣的用法比較好這個問題,大神可以寫出簡明易用的代碼,令人嘆為觀止,而大部分程序員工作多年也搞不清楚,導致項目代碼混亂不堪,慘不忍睹。這些在Java程序員眼中都不是問題,沒有引用指針等方式反而簡單,用JavaBean之類的包裝類傳參輕松愉快不出問題,中規中矩,雖然有時候包裝類太多也會顯得不夠簡潔。
其他
C++不支持的Java特性
包
上面這個程序中定義了一個名為Module的名字空間,為了在main函數中使用名字空間中所定義的方法,使用using指令。
Java有現成的“包”,而C++程序的模塊組織結構,更多需要自己設計與實現。不同項目的結構不一定相同。這在帶來自由度的同時,也變的不及Java方便。接口
? ? ? ?C++中的抽象類,是通過純虛函數實現的,讀起來相對不那么明確。而且有不為人知的用法,比如與大部分人的理解不同,雖然抽象類不可實例化,但純虛函數其實是可以有對應的實現的,并且還有方法調用純虛函數的實現(不要驚訝,家鴿這里沒寫錯),而且這種寫法也是有適用場合的。限于篇幅,這里不做深入描述。后續會專寫一篇探究虛表的文章。這里只是舉例,看一下C++語法的隱晦與詭秘。
Java中,抽象類和抽象方法必須用abstract關鍵字修飾,語法標志清晰明確。規定很明白也很容易理解:一旦類中包含了abstract方法,那類該類必須聲明為abstract類;
抽象類不一定有抽象方法,有抽象方法的類一定是抽象類或者是接口;
抽象類不能實例化,只能被繼承;
抽象類的子類,要么是抽象類,要么重寫抽象類中的所有抽象方法。
? ? ?幾句話說明白,是不是很清楚,而且也藏不了什么特殊用法。二者的不同風格在這個例子中也有所體現。
另一個相關概念是繼承。C++支持多重繼承,因而有了菱形繼承的問題和虛繼承等等概念。這個用法很靈活,也有適用場合。比如需要適配的場景,可以用適配器模式解決問題。它分為兩種,類適配器和對象適配器,類適配器用多重繼承的方式比較合適。但多重繼承引來的問題會很多,一般的規范里都不推薦用。這里也不寫示例代碼了。Java干脆不支持多重繼承,但可以實現多個接口,可以通過實現多個接口模擬多重繼承,達到類似的效果。這一節舉例并在一些具體用法上進行比較,但總體上來說,二者的相似之處還是多于不同之處的;理解并熟悉那些不同之處,可以讓你根據實際工作的需要,在二者之間自如切換,而不會在不知不覺中寫出問題代碼。限于篇幅,二者其他一些重要區別會在本系列后續展開分析。四、多與少
少即是多
?前幾日的國慶閱兵,我們看到了陸軍,海軍,空軍,火箭軍,也看到了雷軍。中國互聯網史的元老人物雷軍,在改革開放民營企業家的彩車方陣中,站在花車的C位出席了慶典。
“專注,極致,口碑,快”是雷軍總結的產品經驗。雷軍認為的“專注"主要是一條,“少即是多”。? ? ? 雷軍曾看到,傳統的軟件研發項目過于復雜,干著干著就干偏了方向,所以一定要把事情簡化到兩、三個月就能做完,而且,這兩、三個月做完可能就解決了某一些用戶的最核心的需求,然后這件事情就可以干了。
專注,是很重要的一件事情,是每天需要不斷地提醒自己的事情。“少就是多”,通常情況下,時間少,資源少,人也很不夠,只能盡可能少做事情,找到關鍵點,以少勝多。用手機舉例,在諾基亞和MOTO的時代,一個手機廠商一年要出60款,甚至100款手機,手機的型號都是很復雜的,沒有人記得住哪個產品的名字。
但從蘋果開始,產品型號簡單的要死,每個產品型號都特別的簡單,但是大家都記住了他的產品型號。“集中所有的精力做一款產品”的成功概率,肯定比“分散精力做100款產品”要高,所以,要集中所有的資源,認認真真做好一兩款產品,這就是“專注”。
于是雷軍認為"砍掉90%,只做10%,認認真真把那10%做好就足夠好"。通常的產品經理,是這個也想做,那個也想做,最后看起來花里胡哨,實際什么都沒做好。優秀的產品經理,不是把需求往多了搞,而是不斷的砍“需求”,專注于最核心的事情。早期的建筑大師路德維希·密斯·凡德羅就提出:“Less is more。”這也是他堅持的哲學,他的設計作品中各個細部精簡到不可精簡的絕對境界,不少作品結構幾乎完全暴露,但是它們高貴、雅致,已使結構本身升華為建筑藝術。
老外說的的"architecture"來源于建筑領域。很多優秀的產品,都是簡潔的代表。一款好的軟件,必然是在滿足需求的大前提下,擁有高適應能力和低復雜度的產品。如何控制復雜度就是軟件工程的一個核心問題,不論業務架構、功能架構還是技術架構。所謂”復雜“不是“深”,而是“多”。通常的軟件常常帶著太過繁多的功能項目,沒有分類整理,有著太過冗雜的交互。設計人員似乎為了彰顯功能的強大,恨不能讓屏幕上滿是按鈕,找個東西很難,仔細分析才發現未必有什么特別厲害的功能。技術實現中需要一次性考慮太多的對象,對象和對象之前的關系也繁多混雜,兩兩間都可能存在著關聯關系,如同一個滿是跳線的電路板,維護修改越來越困難,質量也越來越難以保證,這樣顯然做出不好軟件。反觀Google的大神們,做的頁面只有一個logo和一個搜索框,卻可以在十幾毫秒內中從全世界包羅萬象的海量web信息中獲取到相應主題的內容,并完成相關度計算排序等工作;其中的大數據存儲、處理與檢索技術高難艱深,架構設計卻精巧清晰。是“少即是多”的典型代表。
通過上面“形而下”一節的分析,C++和Java的區別,語言層面上歸結起來,C++是“多”,而Java是“少”。
C++的多,是有歷史原因的。從他誕生那一刻起,它必須兼容C,又要面向對象(或者說基于對象)。隨后的發展要求他支持泛型編程。為了保有市場占有率,確保已有領域的優勢,它必須足夠貼近底層;為了保持活力,又必須吸收現代語言的新特性;與之同時還要保證對歷史代碼的兼容......搞到后來,語言特性越來越多,理論上什么都可以干;而且一段邏輯,可以用多種寫法或不同風格來實現。
什么都可以干,注定了它的強大靈活,作為技術人員,掌握了這種語言,如同手握利器,知識類比遷移起來也容易。但什么都可以干常常意味著什么都容易干不好。就項目而言,針對自己的需求與所屬的領域,很多時候常常有更好的選擇。
語言特性就越來越多,初學者的門檻也越來越高,很少有人敢說自己“精通”C++。這嚴重影響了它的使用與推廣。
Java就是那個“少”。他的設計者吸取了C++語言在項目中遇到的問題,砍掉了容易引入出問題的語言特性,變的“少”起來;在語言層面,一些事情的做法也變的相對單一化了。讓開發、維護與質量保證變得相對簡單,研發效率大幅提升。雖然這樣做也會損失一些性能,使之不再適合做一些領域的事情。但在它擅長的領域(網絡,Web, 企業級應用等),它成為經久不衰的語言,并為后來者(如C#)所仿效。如果把語言當成一種產品,從受歡迎程度和使用率上來說,Java顯然比C++要成功。不少C++程序員并沒有多喜歡C++,而是項目類型和需求決定,必須得使用它。
通常3年的Java程序員,多在討論各種框架,API,設計模式乃至架構思想,可以引入使用成熟的包搭建大數據存儲與處理環境,進行分布式與高并發編程。而通常3年的C++程序員,對開發規范還沒有充分理解,經常碰到不明白的語法特性,在各種奇怪的問題中被考驗著細致與耐心。這就是“少即是多”。
對個人發展而言,藝不壓身,在能夠透徹而全面理解的前提下,“多”會有“多”的優勢,但不能拿這個標準來要求所有人。對團隊而言,新同學需要從“少”開始學起。從這個角度說,“C++規范”起的作用就是:在充分考慮項目需求與具體情況的前提下,通過限制容易出問題的特性和寫法,讓風格與特性由多變少,從而達到代碼復雜度控制與質量保證的效果。在學習C++11及更新的標準時也可以看到,當代C++會不斷的從其他語言中汲取思想和新特性。我們如果有個參照對比和宏觀視角,就會更容易學習與理解。而對于全新的項目,最好是完全摒棄老舊的寫法與特性,新同學更應該從新標準學起,使用一個少而美的子集。To Be Continued......總結
以上是生活随笔為你收集整理的java 析构函数_C++与Java的区别(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: i12蓝牙耳机使用说明书图片_配置强悍、
- 下一篇: 跨设备链路聚合_路由与交换技术(华为设备