自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Ruby 和 Java 的基礎(chǔ)語法比較

開發(fā) 后端
這篇文章示例代碼比較多, Java 程序員可以看到一些 Ruby 相關(guān)語法和使用,Ruby 程序員可以看看 Java 的基本語法和使用方法.

 

本文轉(zhuǎn)載自微信公眾號(hào)「小二十七」,作者肖斌2 。轉(zhuǎn)載本文請(qǐng)聯(lián)系小二十七公眾號(hào)。  

前言

 

這篇文章示例代碼比較多, Java 程序員可以看到一些 Ruby 相關(guān)語法和使用,Ruby 程序員可以看看 Java 的基本語法和使用方法,本文比較長,將近萬字左右,預(yù)計(jì)需要十幾分鐘,如果有耐心讀完文章的話,你將獲得和了解:

  • Ruby 語言的基本語法和使用方式
  • Java 語言的基本語法和使用方式
  • 從老司機(jī)的角度分析和講解 Ruby 和 Java 語言語法的特點(diǎn)和區(qū)別
  • 它們的各自適合并且擅長的應(yīng)用場景

網(wǎng)上單獨(dú)介紹 Ruby ,Java 的文章應(yīng)該很多,但是對(duì)比兩種編程語言的基本語法使用的文章應(yīng)該不多見,寫這篇文章的目的主要是對(duì)自己近期幾個(gè)月學(xué)習(xí) Ruby 做總結(jié)和回顧,我之前最熟悉的編程語言是 Java,我個(gè)人認(rèn)為合格的程序員應(yīng)該掌握多門語言,多學(xué)一門語言沒有壞處,在解決問題的時(shí)候可以多些思路,在經(jīng)歷最近幾個(gè)月的橫向?qū)Ρ群褪褂酶惺?,先拋我個(gè)人結(jié)論,在個(gè)人項(xiàng)目或者小型團(tuán)隊(duì),技術(shù)能力較強(qiáng)的團(tuán)隊(duì)我推薦使用 Ruby, 在團(tuán)隊(duì)需要快速擴(kuò)展和大型項(xiàng)目規(guī)劃的情況下我推薦 Java,因?yàn)榈靡嬗?Java 語法的嚴(yán)謹(jǐn)性和安全性,很大程度上可以保證團(tuán)隊(duì)水平的下限,Java 較強(qiáng)的工程化規(guī)約和代碼類型檢查,可以保證新手不至于寫出破壞性很強(qiáng)的代碼,如果把兩種語言作為一個(gè)簡單的比如,最直觀的感受就是可以把 Ruby 和 Java 比做金庸小說里的兩把武器:

  • Ruby 設(shè)計(jì)精妙,體積小巧靈活迅捷如風(fēng),就像紫薇軟劍那般鋒芒畢露,使用者可以隨心所欲,不必被太多語法和規(guī)則限制
  • Java 老成持重,雖然語法和年代較為古板啰嗦,但是卻長年占據(jù) TIOBE 編程語言排行榜第一名,真可謂是重劍無鋒,大巧不工

在很多人的印象中 Ruby 主要是在初創(chuàng)公司會(huì)比較流行,例如早期的 Airbnb,GitLab 都是使用 Ruby 作為開發(fā)語言,Ruby 是一門很靈活也很優(yōu)雅的動(dòng)態(tài)語言,解釋運(yùn)行,有興趣了解的同學(xué)可以點(diǎn)開 鏈接 查看維基百科的詞條,Ruby 語法精煉,做相同的事情代碼行數(shù)通常會(huì)比 Java 要短的多,使用 Ruby 寫程序的的過程是非常舒服的,因?yàn)椴槐鼐心嘤谀切┛贪鍙?qiáng)制的語法規(guī)范,可以讓開發(fā)者隨心所欲的表達(dá)自己的想法,不必強(qiáng)制分號(hào)結(jié)尾,不強(qiáng)制中括號(hào),不強(qiáng)制方法參數(shù)長度等語法規(guī)范所限制,這種靈活的表達(dá)方式設(shè)計(jì)體現(xiàn)在語言使用的方方面面,并且如果你是用 Mac OS 則系統(tǒng)天生支持 Ruby 開發(fā)環(huán)境,在 Mac 終端 輸入以下命令就可以看到 Ruby 版本號(hào):

  1. ruby -v 
  2. # ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19] 

然后只要在終端里面鍵入 irb 就可以進(jìn)入調(diào)式模式,不像要運(yùn)行 Java 程序首先安裝 JDK 然后再配置環(huán)境變量 JAVA_HOME 和 CLASS_PATH 經(jīng)過繁瑣的配置才勉強(qiáng)可以執(zhí)行 java 命令執(zhí)行 class 程序,在 irb 模式下,對(duì)于簡單的邏輯程序可以先在調(diào)式模式將代碼寫出來驗(yàn)證想法的可行后再加入到代碼庫中去,使用起來非常的方便,示例如下:

  1. >irb 
  2. >2.6.5 :001 > p "hello world" 
  3. # => "hello world" 

下面簡單的寫一個(gè) Hello World 程序進(jìn)行對(duì)比,兩種編程語言在打印 HelloWorld 程序上的寫法,示例代碼如下:

  1. // java print 
  2. public class Main { 
  3.     public static void main(String[] args) { 
  4.         System.out.println("hello world!");   // 注意分號(hào)結(jié)尾 
  5.     } 
  1. # ruby print 
  2. "hello world!" 

通過一個(gè)簡單的 Hello World 程序你就可以發(fā)現(xiàn)兩者的明顯區(qū)別:

  • Ruby 的執(zhí)行是從上到下順序執(zhí)行,main 方法則是 Java 程序的唯一入口
  • Ruby 不必用 ; 號(hào)結(jié)束符,不必使用 {} 聲明代碼塊,函數(shù)式方法傳參甚至不用使用 () (挺有意思)

經(jīng)過以上講解,大家可能會(huì)對(duì)開始產(chǎn)生一些興趣,不過這僅僅只是開始,后面主要簡單介紹一下 Ruby 常用的對(duì)象,條件,循環(huán),方法,運(yùn)算符,數(shù)值,數(shù)組,字符串,散列等使用方法,本文不算嚴(yán)格意義的文章,因?yàn)槭纠a量占了文章的 50% ,而且本文的特點(diǎn)就是會(huì)在語法將 Ruby 和 Java 進(jìn)行對(duì)比,不過還是會(huì)講解 Ruby 基本語法為主,本文偏入門級(jí)水平,介紹的內(nèi)容都是平時(shí)使用比較的多的場景,暫時(shí)不會(huì)涉及到例如 Ruby 的 metaprogramming 和 Java 的 反射等較為深入的知識(shí)點(diǎn),可能后續(xù)會(huì)有單獨(dú)的文章進(jìn)行分析,看完文章應(yīng)該可以用寫一些簡單的程序用于跑一些簡單的腳本應(yīng)該是夠用了,實(shí)際上腳本處理程序也正是 Ruby 很擅長的領(lǐng)域

補(bǔ)充:文章對(duì)比 Java,Ruby 兩種語言在語法上的區(qū)別,并不是爭論哪種編程語言的好壞優(yōu)劣,我個(gè)人觀點(diǎn)是:編程語言本身沒有好壞之分,只有在不同場景下做出合適的選擇,而且熟悉 Java 的同學(xué)也應(yīng)該明白 Java 的優(yōu)勢從來都不在語言和語法層面,而是在生態(tài),并發(fā)編程,虛擬機(jī)和穩(wěn)定這些特性才是 Java 的核心競爭力,在生態(tài)上 Spring Framework為代表的高質(zhì)量輪子覆蓋 Java 應(yīng)用的方方面面,可以說在輪子的多樣性上面,暫時(shí)沒有哪種語言可以跟 Java 抗衡,所以如果簡單的用語法概括語言的好壞就非常以偏概全的看法,話不多說,我們進(jìn)入正題,先列一下文章大綱,入門篇只會(huì)簡單說一些基本語法:

  • 多重賦值
  • 條件判斷
  • 循環(huán)
  • 方法
  • 類和模塊
  • 運(yùn)算符
  • 異常處理

多重賦值

每個(gè)變量單獨(dú)賦值的場景大多相同,就不做介紹,在程序開發(fā)中,我們經(jīng)常會(huì)把多個(gè)變量同時(shí)賦值,這樣效率會(huì)高很多,每種語言對(duì)多重賦值的支持都不同,我們先通過一段代碼對(duì)比 Java,Ruby 語言對(duì)于多重賦值的不同寫法:

  1. // Java 多重賦值 
  2. int a, b, c = 1, 2, 3;      // compile error 
  3. int a, long b, short c = 1, 2L, 3;  // complier error 
  4. int a = 1, b = 2, c =3;     // compile pass  
  1. # Ruby 中多重賦值非常輕松 
  2. a, b, c = 1, 2, 3 
  3. # => [1, 2, 3] 
  4.  
  5. # 兼容不同類型 
  6. a, s, c = 1, "2", 3 
  7. # => [1, "2", 3] 
  8.      
  9. # 兼容不同長度 
  10. a, b, c = 1, 2 
  11. # => [1, 2, nil] 
  12. a, b, c, = 1, 2, 3, 4 
  13. # => [1, 2, 3]  自動(dòng)裁掉超出的長度 

結(jié)合以上案例感覺 Java 對(duì)多重賦值不是很友好,很多不合規(guī)范的語法在編譯期就會(huì)被攔截并且報(bào)錯(cuò),簡單對(duì)比后總結(jié):

  • Java 因?yàn)閺?qiáng)類型,所以對(duì)賦值的比較限制多,例如只能對(duì)同類型的變量進(jìn)行簡單的賦值
  • Ruby 中多重賦值比較輕松,不用考慮類型,長度等問題,過長和過短都不會(huì)在編譯時(shí)拋出問題
  • Ruby 在聲明類型的時(shí)候不需要像 Java 那樣聲明類型,這也是動(dòng)態(tài)語言的特性,我個(gè)人是比較喜歡的

條件判斷

Ruby 的條件判斷主要有以下三種:

  • if 語句
  • unless 語句
  • case 語句

先看實(shí)例和對(duì)比代碼:

  1. a, b = 10, 20 
  2. "a 比 b 大" if a > b 
  3. "a 比 b 小" if a < b 
  4. # => a 比 b 小 
  5.  
  6. # unless 條件就不多做介紹,用法剛好與 if 語句相反,類似java中的 != 運(yùn)算符,實(shí)例代碼: 
  7. a, b = 10, 20 
  8. "a 和 b 不相等" unless a == b 
  9. # => a 和 b 不相等 
  1. int a = 10, b = 20; 
  2. if (a > b) System.out.println("a 比 b 大"); // 在合作項(xiàng)目中還是推薦使用中括號(hào) {} 
  3. if (a < b) System.out.println("a 比 b 小"); 
  4. //=> a 比 b 小 
  5.  
  6. int a = 10, b = 20; 
  7. if (a != b) System.out.println("a 和 b 不相等"); 
  8. //=> a 比 b 小 

還有 case 語句主要用于多條件進(jìn)行判斷,語句用法是 case~when~end 進(jìn)行組合條件判斷,功能跟 Java 中的 switch 相同,還有邏輯運(yùn)算符 ==, !=, ||, && 都是通用的基本知識(shí),所以就不寫詳細(xì)說明和寫示例代碼了,不然會(huì)顯得很啰嗦

總結(jié):條件判斷語句用法非常簡單,兩種編程語言基本類似語言類似,不過還是有以下區(qū)別:

  • Ruby 在關(guān)鍵字選擇上多一些,例如 unless 實(shí)際上是替代了運(yùn)算符 !=,也增加了一些可讀性
  • if 語法基本相似,但 Java 強(qiáng)制表達(dá)式必須使用括號(hào) () ,Ruby則不需要
  • Ruby 使用 if~then~end 語法標(biāo)記代碼塊,不同于 Java 使用中括號(hào) {} 標(biāo)記代碼塊
  • Ruby 條件判斷 if/unless 放在代碼后面,程序看上去可以更加緊湊和簡潔

循環(huán)

Ruby 的循環(huán)結(jié)構(gòu)語句比較豐富,相比 Java 只有 for,while 兩種循環(huán)方式來說,Ruby 中的可用的循環(huán)方法有:time,while,each,for,until,loop,不過大多都異曲同工,就不一一介紹了,本章節(jié)主要圍繞平時(shí)常用的幾個(gè)需求來做一個(gè)簡單的講解,對(duì)比兩種語言的使用區(qū)別,具體如下:

  • 如何執(zhí)行一個(gè)固定次數(shù)的循環(huán) ?
  • 如何遍歷一個(gè)數(shù)組 ?
  • 如何遍歷一個(gè) Hash ?

執(zhí)行固定次數(shù)的循環(huán)是 time循環(huán) 方法的拿手好戲,用于和語句也很簡單,如果不需要下標(biāo)值,|i| 參數(shù)也是可以移除的,示例代碼如下

  1. 3.time do |i|  # i 也可以省略 
  2.    p "第#{i}次打印" 
  3. end 
  4. # => 第0次打印 
  5. # => 第1次打印 
  6. # => 第2次打印 

在 Java 中想要執(zhí)行固定長度的循環(huán),不能通過 forEach只能通過古老的 for..i 來實(shí)現(xiàn),具體代碼如下:

  1. for (int i = 0; i < 3; i++) { 
  2.     System.out.println("第" + i + "次打印"); 
  3. // 第0次打印 
  4. // 第1次打印 
  5. // 第2次打印 

如何遍歷一個(gè)數(shù)組?

在 Ruby 中通常會(huì)推薦使用 **each ** 不僅語法簡單,而且可以輕松拿到元素值,示例代碼如下:

  1. ["abc","efg","hmn"].each do |e| 
  2.   p "#{e}!"  
  3. end 
  4. #=> abc! dfg! hmn! 

Java 在 JDK 8 經(jīng)過 Stream 和 Lambda 語法增強(qiáng)后,遍歷數(shù)組也沒有想象中那么古板,示例代碼:

  1. Arrays.asList("abc""dfg","hmn").forEach(e ->  
  2.   System.out.println(e + "!"
  3. ); 
  4. // abc! dfg! hmn! 

不過在平時(shí)遍歷數(shù)組的時(shí)候經(jīng)常會(huì)遇到一種需求,不僅想要拿到數(shù)組的元素,還需要拿到當(dāng)前循環(huán)的索引值,Ruby 中提供一個(gè)特別的 each 方式實(shí)現(xiàn),就是 each_with_index 方法,它會(huì)把 [元素, 索引] 傳入到 do 代碼塊的后,具體示例代碼:

  1. ["abc","def","ghi"].each_with_index do |e, i| 
  2.   p "當(dāng)前元素 #{e} , 以及第 #{i} 次循環(huán)" 
  3. end 
  4. #=> "當(dāng)前元素 abc , 以及第 0 次循環(huán)" 
  5. #=> ... 

Java 想要實(shí)現(xiàn)相同循環(huán)效果就不能用基于迭代器的 ForEach 實(shí)現(xiàn)了,只能用 for..i 實(shí)現(xiàn),示例代碼如下:

  1. List<String> list = Arrays.asList("abc""deg""ghi"); 
  2. for (int i = 0; i < list.size(); i++) { 
  3.     String e = list.get(i); 
  4.     System.out.println("當(dāng)前元素" + e + ",以及第 " + i + "次循環(huán)"); 
  5. // 當(dāng)前元素abc,以及第 0次循環(huán) 
  6. // .. 

如何遍歷一個(gè) Hash ?

Hash 是 Ruby 的常用的對(duì)象,因此循環(huán)遍歷獲取 K,V 也是相當(dāng)方便的,示例代碼:

  1. hash = {name"apple", age: 15, phone: "15815801580"
  2. hash.each do |k, v| 
  3.  p "key: #{k}, value: #{v}" 
  4. end 
  5. #=> keyname, value: apple 
  6. #=> ... 

Java 中最常用的 K-V 結(jié)構(gòu)的 Hash 實(shí)現(xiàn)是基于 Map 接口的 HashMap,它是一種非線程安全的哈希表實(shí)現(xiàn),之所以常用是因?yàn)樗骖櫟男屎蜁r(shí)間的平衡,內(nèi)部是通過數(shù)組實(shí)現(xiàn),采用使用鏈表法處理哈希沖突,JDK 8 后又引入 紅黑樹 解決哈希沖突過多導(dǎo)致鏈表過長的問題,這塊就先不展開講了,不然可以講很久,示例代碼展示 Java 如何遍歷 Hash:

  1. Map<String, String> hashMap = new HashMap<>(); 
  2. hashMap.put("name""apple"); 
  3. hashMap.put("age""15"); 
  4. hashMap.put("phone""15815801580"); 
  5.  
  6. for (Map.Entry<String, String> entry : hashMap.entrySet()) { 
  7.   System.out.println("key :" + entry.getKey() + ", value : " + entry.getValue()); 
  8. // key :name, value : apple 
  9. // .... 

Java 遍歷 Hash 的方式還有很多種,我們這里只展示最常用的用法,通過 ForEach 遍歷 entrySet() 返回的集合即可。

最后再說一個(gè)有意思的循環(huán)方法,不過使用場景應(yīng)該很少,一個(gè)沒有終止的循環(huán) loop方法,因?yàn)闆]有終止條件,所以必須依賴 break 關(guān)鍵字跳出循環(huán),Java 也可以很輕松實(shí)現(xiàn)這種循環(huán)效果,只是語法上不同而已,我們可以看看以下實(shí)例對(duì)比:

  1. // java 使用 while(true) 或者 for(;;) 實(shí)現(xiàn)無限循環(huán) 
  2. while (true) System.out.println("i use java"); 
  1. # ruby 無限循環(huán) 
  2. loop do 
  3.   p "i use ruby" 
  4. end 

如果程序進(jìn)入無限循環(huán)就只能通過 CTRL + C 來終止程序運(yùn)行了 總結(jié):循環(huán)上兩種語言區(qū)別不大,Ruby 雖然循環(huán)方式多,但是平時(shí)常用的也就 each, for 會(huì)比較多,在循環(huán)上的區(qū)別,大多只是兩種語言在語法上的區(qū)別

方法

分類

Ruby 中的方法大致可分為 3 類:

  • 實(shí)例方法
  • 類方法
  • 函數(shù)式方法

實(shí)例方法:Ruby 中的實(shí)例方法 Instance method 和 Java 中的普通方法類似,顧名思義就是調(diào)用方必須是一個(gè)類的實(shí)例(對(duì)象),需要調(diào)用實(shí)例方法就必須先通過類構(gòu)造一個(gè)實(shí)例對(duì)象才能進(jìn)行調(diào)用,具體請(qǐng)看示例代碼:

  1. # ruby 中的實(shí)例方法 
  2.  [1, 2, 3] .clear # 清理數(shù)組 =>  [] 
  3. 100.to_s   # int 轉(zhuǎn) string  => "100" 
  4. "100".to_i  # string 轉(zhuǎn) int => 100 
  5. ["a""b""c"].index("b")  # 查找下標(biāo) => result: 1 
  1. // java 中的實(shí)例方法 
  2. StringBuilder stringBuilder = new StringBuilder(); 
  3. stringBuilder.append("abc");  // 實(shí)例方法 append 
  4. stringBuilder.append("efg"); 
  5.  
  6. List<String> strList = new ArrayList<>(); 
  7. strList.add("abc");  // 實(shí)例方法 add 
  8. strList.add("efg"); 

類方法:Ruby 的類方法 class method 可以理解為 Java 的靜態(tài)方法,就是需要類對(duì)象作為接收方的方法,指無需構(gòu)建類的對(duì)象即可以直接通過類調(diào)用其自身的方法,大多常見于工具類當(dāng)中,請(qǐng)看示例代碼:

  1. // java 中的靜態(tài)方法 
  2. Arrays.asList(T...a)    // 數(shù)組轉(zhuǎn)集合 
  3. Executors.newCachedThreadPool()  // 創(chuàng)建線程池 
  1. # ruby 中的類方法 
  2. Hash.new  # 創(chuàng)建散列對(duì)象 
  3. Time.new  # 創(chuàng)建時(shí)間對(duì)象 

函數(shù)方法是指沒有接收者的方法,這種類型方法在Java中倒是不存在,參考示例代碼,例如上文中函數(shù)方法 p

  1. "hello" 
  2. puts "print words" 

定義實(shí)例方法

Ruby 定義方法非常簡單,沒有 Java 那么多的格式規(guī)范:修飾符:靜態(tài)聲明:返回值:方法名:(參數(shù)...),在方法聲明的形式上要簡單許多,主要通過 def 關(guān)鍵字定義,具體參考示例代碼:

  1. // java define method 
  2. public static int add(int x, int y) { 
  3.    return x * y 
  4. add(2, 5) 
  5. // 10 
  1. # ruby define method  
  2. def add(x, y) 
  3.     x * y 
  4. end  
  5.  
  6. add(2, 5) 
  7. #=> 10 
  8.  
  9. # 帶默認(rèn)值的方法 
  10. def add(x=2, y=6) 
  11.     x * y 
  12. end 
  13.  
  14. # 省略參數(shù)使用默認(rèn)值調(diào)用方法 
  15. add 
  16. #=> 12 
  17.  
  18. # 指定參數(shù)的方法 
  19. add(2, 5) 
  20. #=> 10 

在方法的命名規(guī)則,兩種語言還有如下區(qū)別:

  • Ruby 方法名可由英文字母,數(shù)字,下劃線組成,但不能以數(shù)字開頭,例如 hello_with_name
  • Java 方法名首字母必須由英文小寫開頭,英文格式遵循駝峰原則,不允許出現(xiàn)連接符,例如 addPerson

返回值return:上面的 ruby 方法并沒有聲明 return 語句也可以拿到返回值,并不代表 ruby 沒有 return 關(guān)鍵字,ruby 有一個(gè)特點(diǎn)就是如果沒有聲明 return 語句那么方法最后一個(gè)表達(dá)式會(huì)成為方法的返回值遵循這個(gè)約定所以上述的方法就可以省略 return 關(guān)鍵字,所以在日常開發(fā)中會(huì)較少的使用 return 關(guān)鍵字

定義類方法

前面講過 Ruby 的類方法實(shí)際上就等于 Java 的靜態(tài)方法,Ruby 中定義類方法的示例代碼:

  1. # ruby 定義類方法 
  2. class Hello 
  3.  
  4.  # class << self 定義類方法的一種方式 
  5.  class << self 
  6.   def say_morning 
  7.    p "hello good morning" 
  8.   end 
  9.  
  10.  #... class << self 塊后面還可以定義多個(gè)類方法 
  11.  end 
  12. end 
  13.  
  14. Hello.say_morning  # 類方法 
  15. #=> "hello good morning" 
  16.    
  17. h = Hello.new 
  18. h.say_morning  # 如果用實(shí)例對(duì)象類方法就會(huì)報(bào)錯(cuò)!undefined method 'say_morning' 

在 Java 中定義靜態(tài)方法的示例:

  1. public class Hello { 
  2.  
  3.     public static void sayMorning() { 
  4.         System.out.println("hello good morning"); 
  5.     } 
  6.  
  7.     public static void main(String[] args) { 
  8.         Hello.sayMorning(); 
  9.     } 
  10.  
  11. #=> "hello good morning" 

定義方法也都是很簡單的知識(shí)點(diǎn),通過以上程序,我們可以得出:

  • Ruby 使用 class << self 或者 class << 類名 可以將代碼塊內(nèi)的方法全部聲明為類方法
  • Java 使用 static 修飾符定義靜態(tài)方法,不能定義塊,我想可能因?yàn)橐?guī)范和可讀性的原因

Ruby 的特點(diǎn)是特定的功能都可以有N種不同的方式實(shí)現(xiàn),所以定義類方法不但只有 class << self ~ end 還可以使用 def class_name.method_name ~ end 這種形式定義 class_name 是類名,method_name 為方法名,如果是在當(dāng)前 class 上下文中可以像示例代碼用同樣的形式 def self.method_name ~ end 定義類方法

方法參數(shù)

默認(rèn)參數(shù) Rudy 的方法默認(rèn)參數(shù)是我個(gè)人比較喜歡的特性,Java 程序里方法參數(shù)是強(qiáng)類型檢查的,就是必須按照參數(shù)定義的類型進(jìn)行傳參,JDK 1.5 后 Java 也出了可變參的特性,不過因?yàn)閷?shí)現(xiàn)效果不是很理性,目前在主流 Java 開發(fā)規(guī)范中還是不被推薦使用的,我們先看一段 Java 定義參數(shù)和使用參數(shù)的示例代碼:

  1. // 方法要求類型,順序,并且必傳 
  2. public void show(String nameint age, String address) { 
  3.     System.out.println(name + ", " + age + ", " + address); 
  4.  
  5. //  new Person().show("胖大海");          // 編譯出錯(cuò),參數(shù)不符合要求 
  6. // new Person().show("胖大海", 19);          // 編譯出錯(cuò),參數(shù)不符合要求 
  7.   new Person().show("胖大海", 19, "北京朝陽區(qū)");     // 編譯通過輸出:胖大海, 19, 北京朝陽區(qū) 

Ruby 則會(huì)靈活一些,具體請(qǐng)看示例代碼:

  1. # 無需聲明類型,帶默認(rèn)值的參數(shù)可不傳 
  2.  def show(name, age=18, address="北京市"
  3.   p "#{name}, #{age}, #{address}" 
  4.  end 
  5.  
  6. Person.new.show("胖胖")  #=> 輸出:胖胖, 18, 北京市 

不過對(duì)于可變參數(shù)兩種語言的實(shí)現(xiàn)幾乎相同,形式上都是對(duì)參數(shù)進(jìn)行特殊標(biāo)記,Java 是通過在參數(shù)前面加...標(biāo)識(shí),ruby 則在參數(shù)前面使用 * 號(hào)標(biāo)識(shí),解釋器會(huì)對(duì)這種語法用數(shù)組進(jìn)行轉(zhuǎn)換,兩者代碼量也差不多,沒有什么差別,簡單看下示例代碼:

  1.  public void names(String ...names) { 
  2.         System.out.println("params :" + Arrays.toString(names)); 
  3.     } 
  4.  
  5.     new Person().names("java""ruby""go""c++");  
  6. // 輸出結(jié)果 :params :[java, ruby, go, c++] 
  1.  def names(*names) 
  2.   p "params: #{names}" 
  3.  end 
  4.  
  5. Person.new.names("java""ruby""go""c++"
  6. # 輸出結(jié)果:params: [\"java\", \"ruby\", \"go\", \"c++\"

簡單通過 2 段代碼的對(duì)比,我們可以對(duì)兩種語言的方法參數(shù)得出以下結(jié)論:

  • Java 方法會(huì)嚴(yán)格按照定義,強(qiáng)制要求類型,值必傳,否則編譯期會(huì)報(bào)錯(cuò),并且無法在聲明時(shí)定義參數(shù)的默認(rèn)值
  • Ruby 方法參數(shù)未設(shè)定默認(rèn)值,不傳參數(shù),只會(huì)在執(zhí)行期報(bào)錯(cuò),但如果聲明時(shí)定義參數(shù)默認(rèn)值,則參數(shù)可不傳
  • Ruby 方法參數(shù)無需定義類型,動(dòng)態(tài)語言的類型大多是推斷出來的
  • 可變參數(shù)兩者實(shí)現(xiàn)方式相同,Java 通過 類型...names 實(shí)現(xiàn),Ruby 通過 *names 語義實(shí)現(xiàn)

方法的基本使用大概就講到這里,函數(shù)方法定義平時(shí)使用不多就暫時(shí)先不聊,繼續(xù)了解還可以看看:定義帶塊的方法,關(guān)鍵字參數(shù)等都是一些語法糖,就不詳細(xì)講解了,接下來聊聊類和模塊

類和模塊

Ruby 也是通過 class 關(guān)鍵字定義類,簡單的用法參考以下代碼:

  1. class Hello 
  2. end 
  3.  
  4. h = Hello.new 

Java 也是通過 class 定義類,不同的是最外層的類 Java 必須使用 public 進(jìn)行修飾,具體請(qǐng)看示例代碼:

  1. public class Hello { 
  2.  
  3.     public static void main(String[] args) { 
  4.         Hello h = new Hello(); 
  5.     } 

那么 Ruby 和 Java 在定義類方面有什么區(qū)別?

  • Ruby 類只有 initialize 構(gòu)造函數(shù),Java 可以根據(jù)參數(shù)不同定義不同的構(gòu)造函數(shù),Java 構(gòu)造函數(shù)必須于類名相同
  • Ruby 和 Java 在類的命名規(guī)則上是一致的,類名必須是首字母大寫開頭
  • Java 通過 public class 修飾類(內(nèi)部類通過 class 修飾),Ruby 則通過 class 修飾類
  • Java 類名必須與文件名相同,Ruby 的文件名和類名不要求強(qiáng)制關(guān)聯(lián)

兩種編程語言在構(gòu)造函數(shù)上對(duì)比的示例代碼:

  1. # 帶構(gòu)造函數(shù)的 Ruby 類 
  2. class Hello 
  3.  def initialize(name="Ruby") # 默認(rèn)參數(shù) 
  4.   @name = name 
  5.  end 
  6.  
  7.  def say_hello  # 實(shí)例方法 
  8.   p "hello #{@name} ! how are you?" 
  9.  end 
  10. end 
  11.  
  12. h = Hello.new("james"
  13. h.say_hello 
  14. #=> "hello james ! how are you?" 
  15. // 帶構(gòu)造函數(shù)的 java 類 
  16. public class Hello { 
  17.  
  18.     private String name
  19.  
  20.    // 有參構(gòu)造函數(shù) 
  21.     public Hello(String youName) { 
  22.         this.name = youName; 
  23.     } 
  24.  
  25.     public void sayHello() { 
  26.         System.out.println("hello! " +name" how are you ?"); 
  27.     } 
  28.  
  29.     public static void main(String[] args) { 
  30.         Hello h = new Hello("jack"); 
  31.         h.sayHello(); 
  32.     } 
  33. #=> "hello! jack how are you ?" 

方法聊到這里,下來聊聊方法里的常量

常量對(duì)比

如果在 Java 和 Ruby 中定義常量,參考示例代碼:

  1. // Java 中定義常量 
  2. public class Hello { 
  3.   // 常量必須是 static final 修飾,代表不可變 
  4.     public static final String VERSION = "1.0"
  5.  
  6.     public static void main(String[] args) { 
  7.        // Hello.VERSION = "1.5";    // 嘗試修改則會(huì)在編譯期報(bào)錯(cuò) 
  8.         System.out.println(Hello.VERSION); 
  9.     } 
  10. #=>"1.0" 
  11. # Ruby 中定義常量 
  12. class PhoneNumber 
  13.  BeiJing = "020" 
  14.  GuangZhou = "021" 
  15. end 
  16.  
  17. p PhoneNumber::BeiJing  #=> "020" 
  18. p PhoneNumber::GuangZhou  #=> "021" 
  19.  
  20. Phonenumber::BeijING = "303"  #=> Ruby 可以修改常量,不會(huì)報(bào)錯(cuò),但會(huì)提示警告 
  21.  
  22. p PhoneNumber.Beijing  #=> ERROR undefined method ! 

在定義常量上的區(qū)別:

  • 命名規(guī)則:Ruby 要求常量首字母大寫,可用駝峰也可全大寫,Java 則要求常量全部大寫,并且必須是 final static 修飾(Java 里的 final 代表不可變,可以聲明類,方法和變量)
  • 調(diào)用方式:Ruby 必須使用 :: 通過類名進(jìn)行外部訪問常量,java 把常量只是當(dāng)成普通的局部變量,使用連接符 . 直接訪問即可
  • 修改變量:Java 不允許修改常量,任何修改的動(dòng)作會(huì)讓編譯器報(bào)錯(cuò) Connot assign a value to final variable 并且無法通過編譯,Ruby 則不同,允許修改常量,但解釋器會(huì)提示警告信息:warning: already initialized constant

訪問級(jí)別

Ruby 和 Java 在方法訪問級(jí)別上沒有什么很大不同,只是 Ruby 沒有包(Package)的概念,所有自然也就沒有 Java 里面的包訪問權(quán)限,細(xì)節(jié)上但是還有些許區(qū)別,Ruby 的三種訪問級(jí)別的定義方法,具體用法直接看示例代碼:

  1. # 定義方法時(shí)聲明訪問權(quán)限 
  2.  private def call_v1 
  3.   p "call_v1 is private" 
  4.  end 
  5.  
  6.  # 定義方法后,再設(shè)定訪問權(quán)限 
  7.  def call_v2 
  8.   p "call_v2 is private" 
  9.  end 
  10.  
  11.  private :call_v2  # 設(shè)定 call_v2 為 private 
  12.  
  13.  # 對(duì)代碼塊設(shè)定, 以下的方法定義為 private  
  14.  private 
  15.  def call_v3 
  16.   p "call_v3 is private" 
  17.  end 
  18.  
  19.  def call_v3 
  20.   p "call_v4 is private" 
  21.  end 

Java 設(shè)定方法訪問級(jí)別的方式則比較單一,只能在定義時(shí)聲明:

  1. private String priv() { 
  2.        return "priv is private"
  3.    } 

綜上所述,兩種語言在訪問級(jí)別的差異和總結(jié):

Java 方法默認(rèn)修飾符是 包訪問權(quán)限

Ruby 方法默認(rèn)訪問級(jí)別是 public(initialize 例外)

Java 方法只能在定義的時(shí)候使用關(guān)鍵字設(shè)定訪問級(jí)別

Ruby 常用的則有三種方式可以設(shè)定方法的訪問級(jí)別,非常靈活

繼承

Ruby 和 Java 的所有類都是基于 Object 的子類,Ruby 則還有更加輕量級(jí)的 BasicObject原始類,這里先不詳細(xì)描述,繼承這個(gè)概念也不多說,面向?qū)ο蟮幕A(chǔ)知識(shí),直接先看兩種語言實(shí)現(xiàn)繼承的方式

Ruby 通過 < 父類名 實(shí)現(xiàn)繼承,示例代碼:

  1. class Car 
  2.  def drive 
  3.   p "car start.." 
  4.  end 
  5. end 
  6.  
  7. class SuperCar < Car 
  8.  def speed_up 
  9.   p "speed to 200km ..." 
  10.  end 
  11. end 
  12.  
  13. s = SuperCar.new 
  14. s.drive 
  15. s.speed_up 
  16.  
  17. #=> "car start.." 
  18. #=> "speed to 200km ..." 

Java 通過 extends實(shí)現(xiàn)繼承的,示例代碼:

  1. class Car { 
  2.     void drive(){ 
  3.         System.out.println("Car Start ..."); 
  4.     } 
  5.  
  6. public class SuperCar extends Car { 
  7.  
  8.     void speedUp() { 
  9.         System.out.println("speed to 200km..."); 
  10.     } 
  11.  
  12.     public static void main(String[] args) { 
  13.         SuperCar c = new SuperCar(); 
  14.         c.drive(); 
  15.         c.speedUp(); 
  16.     } 
  17. // Car Start ... 
  18. // speed to 200km... 

關(guān)于類的繼承方面我們可以得出以下總結(jié):

  • Ruby 通過 < 實(shí)現(xiàn)繼承, Java 通過 extends 關(guān)鍵字實(shí)現(xiàn)繼承
  • Ruby ,Java 在類沒有指定父類的情況下都默認(rèn)繼承 Object類

關(guān)于繼承還有一些經(jīng)驗(yàn)分享的就是,繼承的特性更多用于重寫父類和多態(tài),如果是想要復(fù)用公共的功能,但是類之類沒有明顯的繼承關(guān)系的話,就應(yīng)該遵循組合優(yōu)先大于繼承的原則,不過在 Ruby 中很好的通過 Mix-in 擴(kuò)展解決的繼承這個(gè)問題

模塊和Mix-in

模塊使用 module 關(guān)鍵字創(chuàng)建,命名規(guī)則和類一樣,首字母必須大寫,我們先來看看如何創(chuàng)建模塊

  1. module Display 
  2.  def open 
  3.   p "open display..." 
  4.  end 
  5. end 
  6.  
  7. Display.open # private method `open' called for Display:Module (NoMethodError) 

模塊是 Ruby 的特色功能,定位也很明確,有以下幾個(gè)特點(diǎn):

  • 不能擁有實(shí)例,不能被繼承,所以模塊定位清晰,僅僅表示事物的通用行為
  • 函數(shù)僅僅只能在內(nèi)部被調(diào)用,除非使用 module_function 聲明模塊函數(shù)
  • 模塊更多是結(jié)合 Mix-in 和 include 使用,為類提供增強(qiáng)和更多的可能性

Ruby 中的模塊提供的命名空間 namespace 概念就跟 Java 的包(Package)類似,都是用于區(qū)分相同的類,常量,Mix-in 結(jié)合 include 也就類似 Java 里面的靜態(tài)導(dǎo)入,在 Java 中 import static 可以無需聲明包路徑直接調(diào)用導(dǎo)入類的變量和方法,所謂的 Mix-in 就是利用模塊的抽象能力把非繼承關(guān)系的類之間的共性進(jìn)行抽象和復(fù)用,有些類似 AOP 的概念,可以使代碼既強(qiáng)大又靈活

當(dāng)我們用 OOP 思想對(duì)現(xiàn)實(shí)進(jìn)行抽象的時(shí)候,會(huì)發(fā)現(xiàn)很多非繼承關(guān)系又存在共同功能的事物,例如智能手機(jī),手表繼承自不同的父類,但是他們又都有看時(shí)間 watch_time的能力,我們用代碼展現(xiàn)這種繼承關(guān)系,請(qǐng)看示例代碼:

  1. class Phone 
  2.  def call 
  3.   p "phone call.." 
  4.  end 
  5. end 
  6.  
  7. class IPhone < Phone 
  8.  # iPhone 繼承自 Phone 類,但是不僅可以打電話,還可以看時(shí)間 
  9.  def watch_time 
  10.   p "watch_time 12:00:000" 
  11.  end 
  12. end 
  13.  
  14. class Watch 
  15.  # 手表都可以看時(shí)間 
  16.  def watch_time 
  17.   p "watch_time 12:00:000" 
  18.  end 
  19. end 
  20.  
  21. class AppleWatch < Watch 
  22.  # AppleWatch 不僅看時(shí)間,還有運(yùn)動(dòng)的功能 
  23.  def run 
  24.   p "start run" 
  25.  end 
  26. end 

結(jié)合上面的代碼,我們可以看到 watch_time 代碼重復(fù),對(duì)于不同繼承體系但是相同功能的時(shí)候,就可以用 Mix-in 解決這個(gè)問題,思路如下:

  • 將例如 watch_time 相同的方法和代碼,抽出定義在 module 模塊中
  • 使用 include 引入模塊,將方法引入到實(shí)際的類中

使用 Mix-in 后我們可以看下代碼變化,示例代碼:

  1. module WatchTime 
  2.  # 抽出通用的方法 
  3.  def watch_time 
  4.   p "watch_time 12:00:000" 
  5.  end 
  6. end 
  7.  
  8. class Phone 
  9.  def call 
  10.   p "phone call.." 
  11.  end 
  12. end 
  13.  
  14. class IPhone < Phone 
  15.  include WatchTime 
  16. end 
  17.  
  18. class Watch 
  19.  include WatchTime 
  20. end 
  21.  
  22. class Apple_watch < Watch 
  23.  # apple_watch 不僅看時(shí)間,還可以運(yùn)動(dòng) 
  24.  def run 
  25.   p "start run" 
  26.  end 
  27. end 

使用 Mix-in 這種靈活的語法實(shí)現(xiàn)了魚和熊掌兼得,即沒有破壞繼承結(jié)構(gòu)關(guān)系又實(shí)現(xiàn)共性方法的代碼復(fù)用問題,因?yàn)?Java 沒有 Mix-in 的概念所以就不展示示例代碼了,不過 Java 也有自己的解決方案,而且在 Java 的解決代碼復(fù)用問題通常都應(yīng)該遵循 組合大于繼承 的原則,因?yàn)?Java 的語言設(shè)計(jì)讓繼承更多用于多態(tài)而非復(fù)用

運(yùn)算符

簡單說一下運(yùn)算符,雖然大多編程語言的運(yùn)算符非常的簡單,賦值運(yùn)算,邏輯運(yùn)算,條件運(yùn)算符所有語言的使用方式都幾乎差不多,好像沒什么好講的,但 Ruby 靈活的語法是有不少語法糖,還是可以 Java 程序員羨慕的一下的,假設(shè)一張我們?cè)跇I(yè)務(wù)代碼中經(jīng)常遇到的情況,根據(jù)表達(dá)式取值,當(dāng)表達(dá)式為 true 時(shí)改變變量的值,這種簡單邏輯賦值在 Java 只能這樣寫,請(qǐng)看示例代碼

  1. String value = "abc"
  2. if (condition != null) { 
  3.   value = condition; 
  4. // 看上去很啰嗦 

這種情況在 Ruby 中一行代碼可以實(shí)現(xiàn)相同語義:

  1. # 當(dāng) condition 表達(dá)式為 true 執(zhí)行 value = condition , 否則執(zhí)行 value = "abc" 
  2. value = condition || "abc" 

只所以可以實(shí)現(xiàn)是因?yàn)?Ruby 有一個(gè)不同 Java 的特定, Ruby 對(duì)象都可以用于進(jìn)行布爾表達(dá)式判斷,判斷邏輯為**對(duì)象本身不為 nil 或者 false 表達(dá)式則為 true,否則為 false **

還有一種邏輯則是取相反的情況,例如我們經(jīng)常遇到一種情況是,判斷數(shù)組不為空的時(shí)候取數(shù)組的某一個(gè)下標(biāo),在 Java 中只能這樣寫,示例代碼

  1. List<String> list = Arrays.asList("a""b""c"); 
  2. String item = null
  3. if (list != null) { 
  4.         item = list.get(0); 
  5. // "a" 

這種情況可以用邏輯運(yùn)算符 &&, 它剛好與上面 || 相反,也是一行代碼可以實(shí)現(xiàn)相同功能

  1. str_list = ["a""b""c"
  2. item = str_list && str_list[0] 
  3. #=> "a" 

我個(gè)人非常喜歡這種簡潔的寫法,不過建議在多人項(xiàng)目中不要用太多語法糖,不然可能會(huì)造成項(xiàng)目代碼可讀性混亂

異常處理

很多程序員大部分時(shí)間都花在查錯(cuò)上,所以迅速定位異常很關(guān)鍵,先看看 Ruby 的異常格式 文件名:行號(hào):in 方法名:錯(cuò)誤信息(異常類名) 簡單的用法就不寫示例代碼了,不然占用太多篇幅,兩種語言處理異常方法大同小異,具體處理方式有如下區(qū)別:

  • Ruby 處理異常使用 begin ~ rescue ~ ensure ~ end 這里太簡單就不寫示例代碼了
  • Java 7 使用 try ~ catch ~ finally 到 Java 8 后有了更高效的 try ~ with ~ resources 可自動(dòng)關(guān)閉資源

不過 Ruby 的 Retry 倒是 Java 沒有的特性,它適合對(duì)一些容易出錯(cuò)的程序(例如調(diào)用外部 API )可以進(jìn)行快速重試,具體可以請(qǐng)看示例代碼

  1. class HandleException 
  2.  def zero 
  3.   x, y = 100, 0 
  4.   begin 
  5.    x = x / y 
  6.   rescue 
  7.    p '執(zhí)行異常邏輯' 
  8.    y = 2 
  9.    retry 
  10.   end 
  11.   x 
  12.  end 
  13. end 
  14.  
  15. h = HandleException.new 
  16. p h.zero 
  17.  
  18. #=>"執(zhí)行異常邏輯" 
  19. #=>50 

上述程序非常簡單,大概邏輯是首次執(zhí)行會(huì)拋出異常,然后被 rescue 捕獲后重新復(fù)制,第二次運(yùn)算成功,Java 如果要實(shí)現(xiàn)相同的語義的話,則代碼沒有這么簡潔了,跟上章節(jié)的邏輯運(yùn)算符 &&,|| 類似 resuce 也具備條件表達(dá)式的運(yùn)算,具備很強(qiáng)的表達(dá)能力,我們嘗試對(duì)以上述代碼進(jìn)行一些簡化,示例代碼:

  1. x, y = 100, 0 
  2. x = x / y rescue 50 
  3. # => x = 50 

當(dāng)運(yùn)算 x / y 沒有出現(xiàn)異常則運(yùn)算 x = x / y,當(dāng)出現(xiàn)異常被 resuce 捕獲后則運(yùn)算 x = 50,但相同邏輯在 Java 里面就會(huì)顯得有點(diǎn)啰嗦,請(qǐng)看示例代碼:

  1. int x = 100, y = 0; 
  2. try { 
  3.     x = x / y; 
  4. }catch (ArithmeticException ae) { 
  5.     x = 50; 
  6. // x = 50 

不過像這種小技巧建議只用于簡單的場景,如果業(yè)務(wù)流程復(fù)雜,為了保證代碼清晰易懂還是建議使用標(biāo)準(zhǔn)的 begin ~ rescue ~ ensure ~ end 異常處理語句 ,異常章節(jié)到此結(jié)束,在文章尾部我們總結(jié)一下 Java 和 Ruby 在異常處理的區(qū)別:

  • Ruby 標(biāo)準(zhǔn)異常庫都是繼承 Exception 類,程序通常只能處理 StandarError 異?;蚱渥宇?/li>
  • Java 異常都是繼承 Throwable ,異常被劃分為 Error 異常和 Exception,程序通常只能處理 Exception 的子類 RuntimeException 以及其子類
  • Ruby 支持 retry 從異常中快速重試,rescue 表達(dá)式簡化異常代碼處理,Java 則沒有該功能
  • Java 主動(dòng)拋異常的使用 throw new Exception,而 Ruby 則使用 raise 方法

兩種語言的基本語法對(duì)比就先寫到這里,暫時(shí)就不寫分析和總結(jié)了,因?yàn)槲液罄m(xù)還會(huì)繼續(xù)探索 Ruby 和 Java 在其他使用層面的使用區(qū)別對(duì)比,例如字符串,數(shù)據(jù)類型,集合,哈希,最后想留一個(gè)問題:你覺得靜態(tài)語言和動(dòng)態(tài)語言最明顯的區(qū)別在哪里?他們各自會(huì)存在什么問題?在什么場景下你會(huì)偏向動(dòng)態(tài)語言,什么場景你會(huì)偏向靜態(tài)語言?

責(zé)任編輯:武曉燕 來源: 小二十七
相關(guān)推薦

2009-08-14 13:45:03

Ruby和Python

2011-07-21 13:21:32

PHP

2009-12-18 15:06:10

Ruby常用庫

2009-12-14 13:33:49

Ruby與Python

2010-03-11 11:10:14

Python函數(shù)式

2009-12-14 13:47:28

Ruby簡單語法

2009-09-04 09:00:29

Java基礎(chǔ)語法

2009-12-18 15:56:05

Ruby特殊語法

2010-03-10 19:46:07

Python編程語言

2011-07-06 11:19:45

Objective-C

2009-12-18 17:01:37

Ruby基礎(chǔ)代碼

2021-11-28 18:07:44

PythonRuby編程

2009-07-17 17:49:39

JRuby學(xué)習(xí)

2009-09-09 17:45:25

Ruby的blocks

2011-06-07 11:14:51

JAVAJSP

2009-02-04 09:31:30

SocketNetworkStreTcpClient

2009-07-06 16:01:52

ASP與JSPJSP功能

2014-06-19 10:48:18

RubyPython

2024-04-26 12:56:17

Go編程語言

2009-08-27 11:43:31

C#語法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)