国产美女一级毛片精品久久久|婷婷影院在线综合免费视频|最新国产午夜精品视频成人|久久精品九九无码免费

從Sun離職后,我“拋棄”了Java,擁抱JavaScript和Node

來源:|發(fā)布時(shí)間:2018-10-19 16:04:19

我是前 Sun 公司 Java SE 團(tuán)隊(duì)的一名成員,在工作了 10 多年之后——2009 年 1 月——也就是在甲骨文收購 Sun 公司之前,我離開了公司,然后迷上了 Node.js.

  我對(duì) Node.js 的癡迷到了怎樣的程度?自 2010 年以來,我撰寫了大量有關(guān) Node.js 編程的文章,出版了四本與 Node.js 開發(fā)有關(guān)的書籍,以及與 Node.js 編程有關(guān)的其他書籍和眾多教程。

  在 Sun 公司工作期間,我相信 Java 就是一切。我在 JavaONE 上發(fā)表演講,共同開發(fā)了 java.awt.Robot 類,組織 Mustang 回歸競賽(Java 1.6 版本的漏洞發(fā)現(xiàn)競賽),協(xié)助推出了“Java 發(fā)行許可”,這在后來的 OpenJDK 項(xiàng)目啟動(dòng)過程中起到了一定的作用。我在 java.net(這個(gè)網(wǎng)站現(xiàn)已解散)上每周寫一到兩篇博文,討論 Java 生態(tài)系統(tǒng)中所發(fā)生的主要事件,并堅(jiān)持了 6 年。這些博文的主要主題是關(guān)于“保衛(wèi)”Java,因?yàn)榭傆腥嗽陬A(yù)言 Java 的“死期”。

  在這篇文章中,我將會(huì)解釋我這個(gè) Java 死忠是如何變成一個(gè) Node.js 和 JavaScript 死忠的。

  但其實(shí)我并沒有完全脫離 Java。在過去的三年中,我編寫了大量 Java/Spring/Hibernate 代碼。但兩年的 Spring 編碼經(jīng)歷讓我明白了一個(gè)道理:隱藏復(fù)雜性并不會(huì)帶來簡單性,它只會(huì)產(chǎn)生更多的復(fù)雜性。

  Java 已成為一種負(fù)擔(dān),Node.js 編程卻充滿了樂趣

  有些工具是設(shè)計(jì)師花費(fèi)數(shù)年磨礪和精煉的結(jié)果。他們嘗試不同的想法,去掉不必要的屬性,最終得到一個(gè)只帶有恰到好處屬性的工具。這些工具的簡潔性甚至達(dá)到讓人感到驚艷的程度,但 Java 顯然不屬于這一類。

  Spring 是一個(gè)非常流行的用于開發(fā) Java Web 應(yīng)用程序的框架。Spring(特別是 Spring Boot)的核心目的是成為一個(gè)易于使用的預(yù)配置的 Java EE 棧。Spring 程序員不需要直接接觸 Servlet、數(shù)據(jù)持久化、應(yīng)用程序服務(wù)器就可以獲得一個(gè)完整的系統(tǒng)。Spring 框架負(fù)責(zé)處理所有這些細(xì)節(jié),你只需要把精力放在業(yè)務(wù)編碼上。例如,JPA Repository 類為“findUserByFirstName”方法合成數(shù)據(jù)庫查詢——你不需要編寫任何查詢代碼,只需按照特定方式給方法命名,并添加到 Repository 中即可,Spring 將負(fù)責(zé)處理其余的部分。

  這原本是一個(gè)偉大的故事,一種很好的體驗(yàn),但其實(shí)并不然。

  當(dāng)你遇到 Hibernate 的 PersistentObjectException 時(shí),你知道是哪里出了問題嗎?你可能花了幾天時(shí)間才找到問題所在,導(dǎo)致這個(gè)異常的原因是發(fā)給 REST 端點(diǎn)的 JSON 消息里帶有 ID 字段。Hibernate 想要自己控制 ID 值,所以拋出了這個(gè)令人感到困惑的異常?,這就是過度簡化所帶來的惡果。除了這個(gè),還有其他成千上萬個(gè)同樣令人感到困惑的異常。在 Spring 棧中,一個(gè)子系統(tǒng)套著另一個(gè)子系統(tǒng),它們坐等你犯錯(cuò),然后再拋出應(yīng)用程序崩潰異常來懲罰你。

  然后,你會(huì)看到滿屏的堆棧跟蹤信息,里面滿是這樣那樣的抽象方法。面對(duì)這種級(jí)別的抽象,顯然需要更多的邏輯才能找到你想要的內(nèi)容。如此多的堆棧跟蹤信息不一定是不好的,但它也是在提醒我們:這在內(nèi)存和性能方面的開銷究竟有多大?

  而零代碼的“findUserByFirstName”方法又是如何被執(zhí)行的?Spring 框架必須解析方法名稱,猜測(cè)程序員的意圖,構(gòu)造類似抽象語法樹的東西,生成一些 SQL 語句……那么完成這個(gè)過程需要多少開銷?

  在反反復(fù)復(fù)經(jīng)歷這樣的過程之后,在花了大量時(shí)間學(xué)習(xí)你本不該學(xué)習(xí)的東西之后,你可能會(huì)得出相同的結(jié)論:隱藏復(fù)雜性并不會(huì)帶來簡單性,它只會(huì)產(chǎn)生更多的復(fù)雜性。

  另一面是 Node.js

  Spring 和 Java EE 非常復(fù)雜,而 Node.js 卻是一股清流。首先是 Ryan Dahl 在核心 Node.js 平臺(tái)上所應(yīng)用的設(shè)計(jì)美學(xué)。他追求別樣的東西,花了數(shù)年時(shí)間磨練和改進(jìn)了一系列核心的 Node.js 設(shè)計(jì)理想,最終得到一個(gè)輕量級(jí)的單線程系統(tǒng)。它巧妙地利用了 JavaScript 匿名函數(shù)進(jìn)行異步回調(diào),成為一個(gè)實(shí)現(xiàn)了異步機(jī)制的運(yùn)行時(shí)庫。

  然后是 JavaScript 語言本身。JavaScript 程序員似乎更喜歡無樣板的代碼,這樣他們的意圖才能發(fā)揮作用。

  我們可以通過實(shí)現(xiàn)監(jiān)聽器的例子來說明 Java 和 JavaScript 之間的差別。在 Java 中,監(jiān)聽器需要實(shí)現(xiàn)抽象接口,還需要指定很多啰七八嗦的細(xì)節(jié)。程序員的意圖的這些繁瑣的樣板中漸漸淹沒。

  而在 JavaScript 中,可以使用最簡單的匿名函數(shù)——閉包。你不需要實(shí)現(xiàn)什么抽象接口,只需要編寫所需的代碼,沒有多余的樣板。

  大多數(shù)編程語言都試圖掩蓋程序員的意圖,這讓理解代碼變得更加困難。

  但在 Node.js 中有一點(diǎn)需要注意:回調(diào)地獄。

  沒有完美的解決方案

  在 JavaScript 中,我們一直難以解決兩個(gè)與異步相關(guān)的問題。一個(gè)是 Node.js 中被稱為“回調(diào)地獄”的東西。我們很容易就掉入深層嵌套回調(diào)函數(shù)的陷阱,每個(gè)嵌套都會(huì)使代碼復(fù)雜化,讓錯(cuò)誤和結(jié)果的處理變得更加困難。但 JavaScript 語言并沒有為程序員提供正確表達(dá)異步執(zhí)行的方式。

  于是,出現(xiàn)了一些第三方庫,它們承諾可以簡化異步執(zhí)行。這是另一個(gè)通過隱藏復(fù)雜性帶來更多復(fù)雜性的例子。

const async = require (‘a(chǎn)sync’);const fs = require (‘fs’);const cat = function (filez, fini) {  async.eachSeries (filez, function (filenm, next) {    fs.readFile (filenm, ‘utf8’, function (err, data) {      if (err) return next (err);      process.stdout.write (data, ‘utf8’, function (err) {        if (err) next (err);        else next ();      });    });  },  function (err) {    if (err) fini (err);    else fini ();  });};cat (process.argv.slice (2), function (err) {  if (err) console.error (err.stack);});

  這是個(gè)模仿 Unix cat 命令的例子。async 庫非常適合用于簡化異步執(zhí)行順序,但同時(shí)也引入了一堆模板代碼,從而模糊了程序員的意圖。

  這里實(shí)際上包含了一個(gè)循環(huán),只是沒有使用循環(huán)語句和自然的循環(huán)結(jié)構(gòu)。此外,錯(cuò)誤和結(jié)果的處理邏輯被放在了回調(diào)函數(shù)內(nèi)。在 Node.js 采用 ES 2015 和 ES 2016 之前,我們只能做到這些。

  Node.js 10.x 中,等價(jià)的代碼是這樣的:

const fs = require (‘fs’) .promises;async function cat (filenmz) {  for (var filenm of filenmz) {    let data = await fs.readFile (filenm, ‘utf8’);    await new Promise ((resolve, reject) => {      process.stdout.write (data, ‘utf8’, (err) => {        if (err) reject (err);        else resolve ();      });    });  }}cat (process.argv.slice (2)).catch(err => {     console.error (err.stack); });

  這段代碼使用 async/await 函數(shù)重寫了之前的邏輯。雖然異步邏輯是一樣的,但這次使用了普通的循環(huán)結(jié)構(gòu)。錯(cuò)誤和結(jié)果的處理也顯得很自然。這樣的代碼更容易閱讀,也更容易編碼,程序員的意圖也更容易被理解。

  唯一的瑕疵是 process.stdout.write 沒有提供 Promise 接口,因此用在異步函數(shù)中時(shí)需要丟 Promise 進(jìn)行包裝。

  回調(diào)地獄問題并不是通過隱藏復(fù)雜性才得以解決的。相反,是語言和范式的演變解決了這個(gè)問題。通過使用 async 函數(shù),我們的代碼變得更加美觀。

  通過明確定義的類型和接口提升清晰度

  當(dāng)我還是 Java 的死忠時(shí),我堅(jiān)信嚴(yán)格的類型檢查對(duì)開發(fā)大型的應(yīng)用程序來說是有百利而無一害的。那個(gè)時(shí)候,微服務(wù)的概念還沒有出現(xiàn),也沒有 Docker,人們開發(fā)的都是單體應(yīng)用。因?yàn)?Java 具有嚴(yán)格的類型檢查,所以 Java 編譯器可以幫你避免很多錯(cuò)誤——也就是說可以防止你編譯錯(cuò)誤的代碼。

  相比之下,JavaScript 的類型是松散。程序員不確定他們收到的對(duì)象是什么類型,那么程序員怎么知道該怎么處理這個(gè)對(duì)象?

  但是,Java 的嚴(yán)格類型檢查同樣導(dǎo)致了大量樣板代碼。程序員經(jīng)常需要進(jìn)行類型轉(zhuǎn)換,或以其他方式確保一切都準(zhǔn)確無誤。程序員需要花很時(shí)間確保類型是準(zhǔn)確的,所以使用更多的樣板代碼,希望通過及早捕獲和修復(fù)錯(cuò)誤來節(jié)省時(shí)間。

  程序員不得不使用復(fù)雜的大型 IDE,僅僅使用簡單的編輯器是不行的。IDE 為 Java 程序員提供了一些下拉列表,用于顯示類的可用字段、描述方法的參數(shù),幫助他們構(gòu)建新的類和進(jìn)行重構(gòu)。

  然后,你還得使用 Maven……

  在 JavaScript 中,不需要聲明變量的類型,所以通常不需要進(jìn)行類型轉(zhuǎn)換。因此,代碼更易于閱讀,但可能會(huì)出現(xiàn)未編譯錯(cuò)誤。

  這一點(diǎn)會(huì)讓你更喜歡 Java 還是痛恨 Java,取決于你自己。十年前,我認(rèn)為 Java 的類型系統(tǒng)值得我們花費(fèi)額外的時(shí)間,因?yàn)檫@樣可以獲得更多的確定性。但在今天,我認(rèn)為代價(jià)太大了,使用 JavaScript 會(huì)要簡單得多。

  使用易于測(cè)試的小模塊來掃除 bug

  Node.js 鼓勵(lì)程序員將程序劃分為小單元,也就是模塊。模塊雖小,卻能從一定程度上解決剛剛提到的問題。

  一個(gè)模塊應(yīng)該具備以下特點(diǎn):

  • 自包含——將相關(guān)代碼打包到一個(gè)單元中;

  • 強(qiáng)壯的邊界——模塊內(nèi)部的代碼可以防止外部代碼入侵;

  • 顯式導(dǎo)出——默認(rèn)情況下,代碼和模塊中的數(shù)據(jù)不會(huì)導(dǎo)出,只將選定的函數(shù)和數(shù)據(jù)暴露給外部;

  • 顯式導(dǎo)入——聲明它們依賴哪些模塊;

  • 可能是獨(dú)立的——可以將模塊公開發(fā)布到 npm 存儲(chǔ)庫或其他私有存儲(chǔ)庫,方便在應(yīng)用程序之間共享;

  • 易于理解——更少的代碼意味著更容易理解模塊的用途;

  • 易于測(cè)試——小模塊可以輕松進(jìn)行單元測(cè)試。

  所有這些特點(diǎn)組合在一起,讓 Node.js 模塊更容易測(cè)試,并具有明確定義的范圍。

  人們對(duì) JavaScript 的恐懼源自它缺乏嚴(yán)格的類型檢查,所以可能很容易導(dǎo)致錯(cuò)誤。但在具有清晰邊界的模塊中,受影響代碼被限于模塊內(nèi)部。所以,大多數(shù)問題被安全地隱藏在模塊的邊界內(nèi)。

  松散類型問題的另一個(gè)解決方案是進(jìn)行更多的測(cè)試。

  你必須將節(jié)省下來的一部分時(shí)間(因?yàn)榫帉?JavaScript 代碼更容易)用在測(cè)試上。你的測(cè)試用例必須捕獲編譯器可能捕獲的錯(cuò)誤。

  對(duì)于那些想要在 JavaScript 中使用靜態(tài)檢查類型的人,可以考慮使用 TypeScript。我沒有使用 TypeScript,但聽說它很不錯(cuò)。它與 JavaScript 兼容,同時(shí)提供了有用的類型檢查和其他特性。

  但我們的重點(diǎn)是 Node.js 和 JavaScript。

  包管理

  一想起 Maven 我就頭大。據(jù)說一個(gè)人要么愛它,要么鄙視它,沒有第三種選擇。

  問題是,Java 生態(tài)系統(tǒng)中并沒有一個(gè)核心的包管理系統(tǒng)。Maven 和 Gradle 其實(shí)也很不錯(cuò),但它們并不像 Node.js 的包管理系統(tǒng)那樣有用、可用和強(qiáng)大。

  在 Node.js 世界中,有兩個(gè)優(yōu)秀的包管理系統(tǒng),首先是 npm 和 npm 存儲(chǔ)庫。

  有了 npm,我們就相當(dāng)于有了一個(gè)很好的模式用來描述包依賴性。依賴關(guān)系可以是嚴(yán)格的(指定具體的版本),或者使用通配符表示最新版本。Node.js 社區(qū)已經(jīng)向 npm 存儲(chǔ)庫發(fā)布了數(shù)十萬個(gè)包。

  不僅僅是 Node.js 工程師,前端工程師也可以使用 npm 存儲(chǔ)庫。以前他們使用 Bower,現(xiàn)在 Bower 已被棄用,他們現(xiàn)在可以在 npm 存儲(chǔ)庫中找到所有可用的前端 JavaScript 庫。很多前端框架,如 Vue.js CLI 和 Webpack,都是基于 Node.js 開發(fā)的。

  Node.js 的另一個(gè)包管理系統(tǒng)是 yarn,它也是從 npm 存儲(chǔ)庫中拉取包,并使用與 npm 相同的配置文件。yarn 的主要優(yōu)點(diǎn)運(yùn)行得更快。

  性能

  曾幾何時(shí),Java 和 JavaScript 都因?yàn)檫\(yùn)行速度慢而橫遭指責(zé)。

  它們都需要通過編譯器將源代碼轉(zhuǎn)換為由虛擬機(jī)執(zhí)行的字節(jié)碼。虛擬機(jī)通常會(huì)進(jìn)一步將字節(jié)碼編譯為本地代碼,并使用各種優(yōu)化技術(shù)。

  Java 和 JavaScript 都有很大的動(dòng)機(jī)讓代碼運(yùn)行得更快。在 Java 和 Node.js 中,動(dòng)機(jī)就是讓服務(wù)器端代碼運(yùn)行得更快。而在瀏覽器端,動(dòng)機(jī)是獲得更好的客戶端應(yīng)用程序性能。

  甲骨文的 JDK 使用了 HotSpot,這是一個(gè)具有多種字節(jié)代碼編譯策略的超級(jí)虛擬機(jī)。HotSpot 經(jīng)過高度優(yōu)化,可以生成非常快的代碼。

  至于 JavaScript,我們不禁在想:我們?cè)趺茨芷谕跒g覽器中運(yùn)行的 JavaScript 代碼能夠?qū)崿F(xiàn)復(fù)雜的應(yīng)用程序?基于瀏覽器 JavaScript 實(shí)現(xiàn)辦公文檔處理套件似乎是件不可能實(shí)現(xiàn)的事情?是騾子是馬,拉出來溜溜就知道了。這篇文章是我用谷歌文檔寫的,它性能非常好。瀏覽器端 JavaScript 的性能每年都在飛漲。

  Node.js 直接受益于這一趨勢(shì),因?yàn)樗褂玫氖?Chrome 的 V8 引擎。

  下面是 Peter Marshall 的演講視頻鏈接,他是谷歌的一名工程師,主要負(fù)責(zé) V8 引擎的性能增強(qiáng)工作。他在視頻中描述了為什么 V8 引擎使用 Turbofan 虛擬機(jī)替換了 Crankshaft 虛擬機(jī)。

  V8 引擎中的高性能 JavaScript:https://youtu.be/YqOhBezMx1o

  在機(jī)器學(xué)習(xí)領(lǐng)域,數(shù)據(jù)科學(xué)家通常使用R語言或 Python,因?yàn)樗麄兪忠蕾嚳焖贁?shù)值計(jì)算。但由于各種原因,JavaScript 在這方面表現(xiàn)很差。不過,有人正在開發(fā)一種用于數(shù)值計(jì)算的標(biāo)準(zhǔn) JavaScript 庫。

  JavaScript 中的數(shù)值計(jì)算:https://youtu.be/1ORaKEzlnys

  另一個(gè)視頻演示了如何通過 TensorFlow.js 在 JavaScript 中使用 TensorFlow。它提供了一個(gè)類似于 TensorFlow Python 的 API,可以導(dǎo)入預(yù)訓(xùn)練模型。它運(yùn)行在瀏覽器中,可用于分析實(shí)時(shí)視頻,從中識(shí)別出經(jīng)過訓(xùn)練的對(duì)象。

  基于 JavaScript 的機(jī)器學(xué)習(xí):https://youtu.be/YB-kfeNIPCE

  在另一個(gè)演講視頻中,IBM 的 Chris Bailey 討論了 Node.js 的性能和可伸縮性問題,特別是在 Docker/Kubernetes 部署方面。他從一組基準(zhǔn)測(cè)試開始,演示了 Node.js 在I/O吞吐量、應(yīng)用程序啟動(dòng)時(shí)間和內(nèi)存占用方面遠(yuǎn)遠(yuǎn)超過 Spring Boot。此外,得益于 V8 引擎的改進(jìn),Node.js 每次發(fā)布的新版在性能方面都有顯著的提升。

  Node.js 的性能和高度可伸縮的微服務(wù):https://youtu.be/Fbhhc4jtGW4

  在上面的這個(gè)視頻中,Bailey 說我們不應(yīng)該在 Node.js 中運(yùn)行計(jì)算密集型的代碼。因?yàn)?Node.js 采用了單線程模型,長時(shí)間運(yùn)行計(jì)算密集型任務(wù)會(huì)導(dǎo)致事件阻塞。

  如果 JavaScript 的改進(jìn)還無法滿足你的應(yīng)用程序的要求,還有其他兩種方法可以將本地代碼直接集成到 Node.js 中。最直接的方法是使用 Node.js 本地代碼模塊。Node.js 工具鏈中包含了 node-gyp,可用于處理與本地代碼模塊的鏈接。下面的視頻演示了如何集成 Rust 庫和 Node.js:

  JavaScript 與 Rust 集成,遠(yuǎn)比你想象得簡單:https://youtu.be/Pfbw4YPrwf4

  WebAssembly 可以將其他語言編譯為運(yùn)行速度非常快的 JavaScript 子集。WebAssembly 是一種可在 JavaScript 引擎內(nèi)運(yùn)行的可執(zhí)行代碼的可移植格式。下面的視頻做了一個(gè)很好的概述,并演示了如何使用 WebAssembly 在 Node.js 中運(yùn)行代碼。

  在 NodeJS 中使用 WebAssembly:https://youtu.be/hYrg3GNn1As

  富 Internet 應(yīng)用程序(RIA)

  十年前,軟件行業(yè)一直熱議利用快速的 JavaScript 引擎實(shí)現(xiàn)富 Internet 應(yīng)用程序,從而取代桌面應(yīng)用程序。

  這個(gè)故事實(shí)際上在二十多年前就已經(jīng)開始了。Sun 公司和 Netscape 公司達(dá)成了共識(shí),在 Netscape Navigator 中使用 Java 小程序(Applet)。JavaScript 語言在某種程度上是作為 Java 小程序的腳本語言而開發(fā)出來的。服務(wù)器端有 Java Servlet,客戶端有 Java Applet,這樣就可以在兩端使用同樣的一門編程語言。然而,由于各種原因,這種美好的愿望并沒有實(shí)現(xiàn)。

  十年前,JavaScript 開始變得足夠強(qiáng)大,可以實(shí)現(xiàn)復(fù)雜的應(yīng)用程序。因此,RIA 被認(rèn)為是 Java 客戶端應(yīng)用程序的終結(jié)者。

  今天,我們開始看到 RIA 的想法得以實(shí)現(xiàn)。服務(wù)器端的 Node.js 和兩端都有的 JavaScript 讓這一切成為可能。

  當(dāng)然,Java 作為桌面應(yīng)用程序平臺(tái)的消亡并不是因?yàn)?JavaScript RIA,而是因?yàn)?Sun 公司忽視了客戶端技術(shù)。Sun 公司把注意力放在要求快速服務(wù)器端性能的企業(yè)客戶身上。當(dāng)時(shí)我還在 Sun 公司任職,我親眼看著這件事情發(fā)生。真正殺死 Applet 的是幾年前在 Java 插件和 Java Web Start 中發(fā)現(xiàn)的一個(gè)安全漏洞。這個(gè)漏洞導(dǎo)致全球一致呼吁停止使用 Java Applet 和 Java Web Start 應(yīng)用程序。

  我們?nèi)匀豢梢蚤_發(fā)其他類型的 Java 桌面應(yīng)用程序,NetBeans 和 Eclipse IDE 之間的競爭仍然存在。但是,Java 在這個(gè)領(lǐng)域工作是停滯不前的,除了開發(fā)工具之外,很少有基于 Java 的應(yīng)用程序。

  JavaFX 是個(gè)例外。

  10 年前,JavaFX 意欲成為 Sun 公司對(duì) iPhone 的反擊。它用于開發(fā)基于 Java 的手機(jī) GUI 應(yīng)用程序,想把 Flash 和 iOS 應(yīng)用程序打垮。然而,這一切都沒有發(fā)生。JavaFX 現(xiàn)在仍然可以使用,但沒有了當(dāng)初的喧囂。

  這個(gè)領(lǐng)域的所有興奮點(diǎn)都發(fā)生在 React、Vue.js 和類似的框架上,JavaScript 和 Node.js 在很大程度上要得益于此。

  結(jié)論

  現(xiàn)在,開發(fā)服務(wù)器端應(yīng)用程序有很多選擇。我們不再局限于“P”開頭的語言(Perl、PHP、Python)和 Java,我們還有 Node.js、Ruby、Haskell、Go、Rust 等等。

  至于為什么我會(huì)轉(zhuǎn)向 Node.js,很明顯,我更喜歡在使用 Node.js 編程時(shí)的那種自由的感覺。Java 成了負(fù)擔(dān),而 Node.js 沒有這樣的負(fù)擔(dān)。如果我再次拿起 Java,那肯定是因?yàn)橛腥烁读隋X。

  每個(gè)應(yīng)用程序都有其真實(shí)需求。只是因?yàn)閭(gè)人喜歡而一直使用 Node.js 也不見得是對(duì)的。在選擇一門語言或一個(gè)框架時(shí)總歸是有技術(shù)方面的考量的。例如,我最近完成的一些工作涉及 XBRL 文檔,由于最好的 XBRL 庫是用 Python 實(shí)現(xiàn)的,所以就有必要學(xué)習(xí) Python。


上一篇:甲骨文Java案仍在拉鋸 谷歌誓要向最高法院上訴
下一篇:Java全棧工程師“真硬核”帶你快速跳槽轉(zhuǎn)行

熱門話題

招生熱線: 4008-0731-86 / 0731-82186801

學(xué)校地址: 長沙市天心區(qū)團(tuán)結(jié)路6號(hào)

Copyright © 2006 | 湖南大計(jì)信息科技有限公司 版權(quán)所有

湘ICP備14017520號(hào)-3

關(guān)注我們
在線咨詢
嘿,我來幫您!