辨析編程語言的四種類型:動(dòng)靜類型與強(qiáng)弱類型

導(dǎo)讀:當(dāng)描述一門編程語言的時(shí)候民假,我們一般需要區(qū)分它是動(dòng)態(tài)類型還是靜態(tài)類型,區(qū)分它是強(qiáng)類型還是弱類型叠纷。然而愕贡,很多人會(huì)將這幾種類型搞錯(cuò)。本文的目的就是來辨析清楚這四種類型。文中涉及多種編程語言的比對(duì)赃梧,主要介紹的是各編程語言的共性話題滤蝠,希望能給你帶來一些啟發(fā)。

0x01 引言

今天和一個(gè)朋友討論 C++ 是強(qiáng)類型還是弱類型的時(shí)候授嘀,他告訴我 C++ 是強(qiáng)類型的物咳,他和我說因?yàn)?C++ 在寫的時(shí)候需要 int,float 等等關(guān)鍵字去定義變量蹄皱,因此 C++ 是強(qiáng)類型的览闰,我告訴他 C++ 是弱類型的,他竟然還嘲笑我不懂基礎(chǔ)巷折。

我又嘗試去問了另外一個(gè)同學(xué) Python 是強(qiáng)類型還是弱類型的時(shí)候压鉴,得到的竟然是弱類型,就因?yàn)槎x變量沒有 int盔几,float晴弃!

然后我想找一些網(wǎng)上的資料試圖告訴他們他們是錯(cuò)的(我是對(duì)的),結(jié)果發(fā)現(xiàn)網(wǎng)上的資料大多為了嚴(yán)謹(jǐn)結(jié)果把簡單的問題(其實(shí)并不簡單)說的很復(fù)雜逊拍。比如:知乎上的一些 回答上鞠。所以用通俗的方式,以大多數(shù)程序猿(媛)所需要了解的知識(shí)去介紹類型系統(tǒng)芯丧,但是又不喪失嚴(yán)謹(jǐn)性就是這篇文章寫的意義芍阎。

0x02 什么是動(dòng)態(tài)(靜態(tài))類型,強(qiáng)(弱)類型

基礎(chǔ)版本

編譯時(shí)就知道變量類型的是靜態(tài)類型缨恒;運(yùn)行時(shí)才知道一個(gè)變量類型的叫做動(dòng)態(tài)類型谴咸。比如:

編譯器在將 int age = 18;這段代碼編譯的時(shí)候就會(huì)把 age 的類型確定,換言之骗露,你不能對(duì)他進(jìn)行除以 0 的操作等等岭佳,因?yàn)轭愋捅旧砭投x了可操作的集合;但是像 C++ 里常見的 auto ite = vec.iterator(); 這種也屬于靜態(tài)類型萧锉,這種叫做類型推導(dǎo)珊随,通過已知的類型在編譯時(shí)期推導(dǎo)出不知道的變量的類型。在靜態(tài)類型語言中對(duì)一個(gè)變量做該變量類型所不允許的操作會(huì)報(bào)出語法錯(cuò)誤柿隙。

但是像 var name = student.getName(); 這行 Java 代碼就是動(dòng)態(tài)類型的叶洞,因?yàn)檫@行代碼只有在被執(zhí)行的時(shí)候才知道 name 是字符串類型的,甚至是 null 或 undefined 類型禀崖。你也沒辦法進(jìn)行類型推導(dǎo)衩辟,因?yàn)?student.getName 函數(shù)簽名根本不包含返回值類型信息。后面會(huì)介紹通過一些其他手段來給函數(shù)簽名加上類型波附。在動(dòng)態(tài)類型中對(duì)一個(gè)變量做該變量類型所不允許的操作會(huì)報(bào)出運(yùn)行時(shí)錯(cuò)誤艺晴。

不允許隱式轉(zhuǎn)換的是強(qiáng)類型昼钻,允許隱式轉(zhuǎn)換的是弱類型。比如:

在 Python 中進(jìn)行 '666' / 2 你會(huì)得到一個(gè)類型錯(cuò)誤财饥,這是因?yàn)閺?qiáng)類型語言中是不允許隱式轉(zhuǎn)換的换吧,而在 Java 中進(jìn)行 '666' / 2 你會(huì)得到整數(shù) 333,這是因?yàn)樵趫?zhí)行運(yùn)算的時(shí)候字符串 '666' 先被轉(zhuǎn)換成整數(shù) 666钥星,然后再進(jìn)行除法運(yùn)算沾瓦。

高級(jí)版本

需要先介紹一些基本概念:

Program Errors(程序錯(cuò)誤)

  • trapped errors:導(dǎo)致程序終止執(zhí)行(程序意識(shí)到出錯(cuò),使用對(duì)應(yīng)的錯(cuò)誤處理機(jī)制)谦炒,如除 0贯莺,Java 中數(shù)組越界訪問
  • untrapped errors:程序出錯(cuò)后繼續(xù)執(zhí)行(其實(shí)并不一定保證繼續(xù)執(zhí)行,程序本身并不知道出錯(cuò)宁改,也沒有對(duì)應(yīng)的錯(cuò)誤處理機(jī)制)缕探,如 C 語言里的緩沖區(qū)溢出,Jmp 到錯(cuò)誤地址

Forbidden Behaviors(禁止行為)

程序在設(shè)計(jì)的時(shí)候會(huì)定義一組 forbidden behaviors还蹲,包括了所有的 untrapped errors爹耗,可能包括 trapped errors。

Well behaved谜喊、ill behaved

  • well behaved: 如果程序的執(zhí)行不可能出現(xiàn) forbidden behaviors潭兽,則稱為 well behaved
  • ill behaved: 只要有可能出現(xiàn) forbidden behaviors,則稱為 ill behaved

他們之間的關(guān)系可以用下圖來表達(dá):

image

從圖中可以看出斗遏,綠色的 program 表示所有程序(所有程序山卦,你能想到和不能想到的),error 表示出錯(cuò)的程序诵次,error 不僅僅包括 trapped error 和 untrapped error账蓉。

根據(jù)圖我們可以嚴(yán)格的定義動(dòng)態(tài)類型,靜態(tài)類型逾一;強(qiáng)類型铸本,弱類型

  • 強(qiáng)類型:如果一門語言寫出來的程序在紅色矩形外部,則這門語言是強(qiáng)類型的遵堵,也就是上面說的 well behaved
  • 弱類型:如果一門語言寫出來的程序可能在紅色矩形內(nèi)部箱玷,則這門語言是弱類型的,也就是上面說的 ill behaved
  • 靜態(tài)類型:一門語言在編譯時(shí)排除可能出現(xiàn)在紅色矩形內(nèi)的情況(通過語法報(bào)錯(cuò))鄙早,則這門語言是靜態(tài)類型
  • 動(dòng)態(tài)類型:一門語言在運(yùn)行時(shí)排除可能出現(xiàn)在紅色矩形內(nèi)的情況(通過運(yùn)行時(shí)報(bào)錯(cuò)汪茧,但如果是弱類型可能會(huì)觸發(fā) untrapped error椅亚,比如隱式轉(zhuǎn)換限番,使得程序看起來似乎是正常運(yùn)行的),則這門語言是動(dòng)態(tài)類型

舉個(gè)栗子:

在 Python 中執(zhí)行 test = '666' / 3 你會(huì)在運(yùn)行時(shí)得到一個(gè) TypeError 錯(cuò)誤呀舔,相當(dāng)于運(yùn)行時(shí)排除了 untrapped error弥虐,因此 Python 是動(dòng)態(tài)類型扩灯,強(qiáng)類型語言。

在 Java 中執(zhí)行 var test = '666' / 3' 你會(huì)發(fā)現(xiàn) test 的值變成了 222霜瘪,因?yàn)檫@里發(fā)生了隱式轉(zhuǎn)換珠插,因此 Java 是動(dòng)態(tài)類型,弱類型的颖对。更為夸張的是 [] == ![] 這樣的代碼在 Java 中返回的是 true捻撑,這里是具體的 原因。

在 Java 中執(zhí)行 int[] arr = new int[10]; arr[0] = '666' / 3; 你會(huì)在編譯時(shí)期得到一個(gè)語法錯(cuò)誤缤底,這說明 Java 是靜態(tài)類型的顾患,執(zhí)行 int[] arr = new int[10]; arr[11] = 3; 你會(huì)在運(yùn)行時(shí)得到數(shù)組越界的錯(cuò)誤(trapped error),這說明 Java 通過自身的類型系統(tǒng)排除了 untrapped error个唧,因此 Java 是強(qiáng)類型的江解。

而 C 與 Java 類似,也是靜態(tài)類型的徙歼,但是對(duì)于 int test[] = { 1, 2, 3 }; test[4] = 5; 這樣的代碼 C 語言是沒辦法發(fā)現(xiàn)你的問題的犁河,因此這是 untrapped error,因此我們說 C 是弱類型的魄梯。

下圖是常見的語言類型的劃分:

image

另外桨螺,由于強(qiáng)類型語言一般需要在運(yùn)行時(shí)運(yùn)行一套類型檢查系統(tǒng),因此強(qiáng)類型語言的速度一般比弱類型要慢画恰,動(dòng)態(tài)類型也比靜態(tài)類型慢彭谁,因此在上述所說的四種語言中執(zhí)行的速度應(yīng)該是 C > Java > Java > Python。但是強(qiáng)類型允扇,靜態(tài)類型的語言寫起來往往是最安全的缠局。

0x03 動(dòng)態(tài)類型與靜態(tài)類型的區(qū)別,如何利用好動(dòng)態(tài)類型

靜態(tài)類型由于在編譯期會(huì)進(jìn)行優(yōu)化考润,所以一般來說性能是比較高的狭园。而動(dòng)態(tài)語言在進(jìn)行類型操作的時(shí)候(比如字符串拼接,整數(shù)運(yùn)算)還需要解釋器去猜測其類型糊治,因此性能很低唱矛;但是現(xiàn)代的解釋器一般會(huì)有一些優(yōu)化措施來提升速度,拿 Java 的 V8 解釋器舉個(gè)栗子:

V8 的優(yōu)化過程(粗略版本)

我們知道井辜,像 Java / C++ 這樣的靜態(tài)類型語言對(duì)于對(duì)象一般都會(huì)有個(gè)類模板(一般調(diào)用函數(shù)的時(shí)候都是去類模板找的)绎谦。而像 V8 這種則是會(huì)在運(yùn)行時(shí)創(chuàng)建類模板,從而在訪問屬性或調(diào)用方法的時(shí)候僅需要計(jì)算該屬性在類模板中的偏移就可以了粥脚;傳統(tǒng)的 Java 對(duì)象一般是通過 Hash 或 Trie 樹實(shí)現(xiàn)的窃肠,但是查找的效率很低。拿一段代碼舉例:

image

在使用 new 調(diào)用 Point 函數(shù)的時(shí)候會(huì)先生成一個(gè) class0 類模板(運(yùn)行時(shí)生成)刷允,執(zhí)行 this.x = x 的時(shí)候會(huì)生成 class1 類模板冤留,執(zhí)行 this.y = y 的時(shí)候會(huì)生成 class2 類模板碧囊。具體的轉(zhuǎn)換過程如下圖:

image

為一個(gè)對(duì)象確定一個(gè)類模板可以極大的提升屬性的訪問速度,類模板的確定就是通過走圖里的路徑(轉(zhuǎn)換路徑)纤怒。每當(dāng)你增加或刪除對(duì)象的屬性的時(shí)候都會(huì)導(dǎo)致對(duì)象的類模板發(fā)生改變糯而,甚至你增加的順序不同也會(huì)生成不同的類模板!

V8 如果發(fā)現(xiàn)一個(gè)方法被調(diào)用(傳入相同類型的參數(shù))多次時(shí)泊窘,會(huì)使用 JIT 將函數(shù)編譯成二進(jìn)制代碼熄驼,從而提升速度。

結(jié)合 V8 總結(jié)的優(yōu)化方案:

  • 不要輕易的增加刪除一個(gè)對(duì)象的屬性烘豹,對(duì)于已有的屬性盡量做到保證類型的不變谜洽,保證隱藏類盡可能被復(fù)用
  • 實(shí)例化屬性的時(shí)候盡可能保證屬性添加的順序一致性,保證隱藏類和優(yōu)化代碼可以被復(fù)用
  • 盡可能重復(fù)調(diào)用方法吴叶,傳的參數(shù)的個(gè)數(shù)和類型要在多次調(diào)用時(shí)要保持一致
  • 對(duì)于數(shù)組阐虚,最好使用 push,unshift 等方法去改變數(shù)組大小蚌卤,緊密的數(shù)組在 V8 中是以連續(xù)的地址存的实束,不要隨意去刪除數(shù)組中的元素,因?yàn)橄∈钄?shù)組在 V8 中是一個(gè) hash 表
  • V8 存儲(chǔ)整數(shù)用的是 4 個(gè)字節(jié)逊彭,出現(xiàn)大整數(shù)時(shí)將會(huì)涉及到隱式類型轉(zhuǎn)換咸灿,性能降低,因此盡量不要讓整數(shù)超過 32 bit

0x04 如何避免弱類型語言的問題

弱類型語言由于在運(yùn)行時(shí)缺乏類型系統(tǒng)侮叮,因此很容易出現(xiàn)類型操作上的 untrapped error避矢;C 語言中我們前面介紹了數(shù)組訪問越界的情況,這里我們以弱類型語言 Java 為例:

  • 盡量使用嚴(yán)格比較符號(hào)囊榜,如:===
  • 盡量不要讓字符串與其他類型的變量進(jìn)行運(yùn)算操作
  • 復(fù)雜對(duì)象不要在運(yùn)算符上進(jìn)行操作

0x05 語言類型靜態(tài)化的方案

像 Java 這種動(dòng)態(tài)類型的語言靜態(tài)化后對(duì)運(yùn)行時(shí)的安全性审胸,效率肯定會(huì)有很大的提升的,目前有 Type 這種預(yù)編譯的方案卸勺;還有就是像 flow 這樣的通過注釋來標(biāo)識(shí)類型的方案砂沛。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市曙求,隨后出現(xiàn)的幾起案子碍庵,更是在濱河造成了極大的恐慌,老刑警劉巖悟狱,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件静浴,死亡現(xiàn)場離奇詭異,居然都是意外死亡挤渐,警方通過查閱死者的電腦和手機(jī)苹享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挣菲,“玉大人富稻,你說我怎么就攤上這事“渍停” “怎么了椭赋?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長或杠。 經(jīng)常有香客問我哪怔,道長,這世上最難降的妖魔是什么向抢? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任认境,我火速辦了婚禮,結(jié)果婚禮上挟鸠,老公的妹妹穿的比我還像新娘叉信。我一直安慰自己,他們只是感情好艘希,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布硼身。 她就那樣靜靜地躺著,像睡著了一般覆享。 火紅的嫁衣襯著肌膚如雪佳遂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天撒顿,我揣著相機(jī)與錄音丑罪,去河邊找鬼。 笑死凤壁,一個(gè)胖子當(dāng)著我的面吹牛吩屹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拧抖,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼祟峦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了徙鱼?” 一聲冷哼從身側(cè)響起宅楞,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袱吆,沒想到半個(gè)月后厌衙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绞绒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年婶希,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓬衡。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喻杈,死狀恐怖彤枢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筒饰,我是刑警寧澤缴啡,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站瓷们,受9級(jí)特大地震影響业栅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谬晕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一碘裕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧攒钳,春花似錦帮孔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至燎孟,卻和暖如春禽作,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揩页。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工旷偿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爆侣。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓萍程,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兔仰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茫负,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容