您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Ruby和Java的基礎(chǔ)語法有哪些區(qū)別”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Ruby和Java的基礎(chǔ)語法有哪些區(qū)別”吧!
前言
這篇文章示例代碼比較多, 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)該很多,但是對比兩種編程語言的基本語法使用的文章應(yīng)該不多見,寫這篇文章的目的主要是對自己近期幾個(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)公司會比較流行,例如早期的 Airbnb,GitLab 都是使用 Ruby 作為開發(fā)語言,Ruby 是一門很靈活也很優(yōu)雅的動(dòng)態(tài)語言,解釋運(yùn)行,有興趣了解的同學(xué)可以點(diǎn)開 鏈接 查看維基百科的詞條,Ruby 語法精煉,做相同的事情代碼行數(shù)通常會比 Java 要短的多,使用 Ruby 寫程序的的過程是非常舒服的,因?yàn)椴槐鼐心嘤谀切┛贪鍙?qiáng)制的語法規(guī)范,可以讓開發(fā)者隨心所欲的表達(dá)自己的想法,不必強(qiáng)制分號結(jié)尾,不強(qiáng)制中括號,不強(qiáng)制方法參數(shù)長度等語法規(guī)范所限制,這種靈活的表達(dá)方式設(shè)計(jì)體現(xiàn)在語言使用的方方面面,并且如果你是用 Mac OS 則系統(tǒng)天生支持 Ruby 開發(fā)環(huán)境,在 Mac 終端 輸入以下命令就可以看到 Ruby 版本號:
ruby -v # 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 模式下,對于簡單的邏輯程序可以先在調(diào)式模式將代碼寫出來驗(yàn)證想法的可行后再加入到代碼庫中去,使用起來非常的方便,示例如下:
>irb >2.6.5 :001 > p "hello world" # => "hello world"
下面簡單的寫一個(gè) Hello World 程序進(jìn)行對比,兩種編程語言在打印 HelloWorld 程序上的寫法,示例代碼如下:
// java print public class Main { public static void main(String[] args) { System.out.println("hello world!"); // 注意分號結(jié)尾 } }
# ruby print p "hello world!"
通過一個(gè)簡單的 Hello World 程序你就可以發(fā)現(xiàn)兩者的明顯區(qū)別:
Ruby 的執(zhí)行是從上到下順序執(zhí)行,main 方法則是 Java 程序的唯一入口
Ruby 不必用 ; 號結(jié)束符,不必使用 {} 聲明代碼塊,函數(shù)式方法傳參甚至不用使用 () (挺有意思)
經(jīng)過以上講解,大家可能會對開始產(chǎn)生一些興趣,不過這僅僅只是開始,后面主要簡單介紹一下 Ruby 常用的對象,條件,循環(huán),方法,運(yùn)算符,數(shù)值,數(shù)組,字符串,散列等使用方法,本文不算嚴(yán)格意義的文章,因?yàn)槭纠a量占了文章的 50% ,而且本文的特點(diǎn)就是會在語法將 Ruby 和 Java 進(jìn)行對比,不過還是會講解 Ruby 基本語法為主,本文偏入門級水平,介紹的內(nèi)容都是平時(shí)使用比較的多的場景,暫時(shí)不會涉及到例如 Ruby 的 metaprogramming 和 Java 的 反射等較為深入的知識點(diǎn),可能后續(xù)會有單獨(dú)的文章進(jìn)行分析,看完文章應(yīng)該可以用寫一些簡單的程序用于跑一些簡單的腳本應(yīng)該是夠用了,實(shí)際上腳本處理程序也正是 Ruby 很擅長的領(lǐng)域
補(bǔ)充:文章對比 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án)
方法
類和模塊
運(yùn)算符
異常處理
多重賦值
每個(gè)變量單獨(dú)賦值的場景大多相同,就不做介紹,在程序開發(fā)中,我們經(jīng)常會把多個(gè)變量同時(shí)賦值,這樣效率會高很多,每種語言對多重賦值的支持都不同,我們先通過一段代碼對比 Java,Ruby 語言對于多重賦值的不同寫法:
// Java 多重賦值 int a, b, c = 1, 2, 3; // compile error int a, long b, short c = 1, 2L, 3; // complier error int a = 1, b = 2, c =3; // compile pass
# Ruby 中多重賦值非常輕松 a, b, c = 1, 2, 3 # => [1, 2, 3] # 兼容不同類型 a, s, c = 1, "2", 3 # => [1, "2", 3] # 兼容不同長度 a, b, c = 1, 2 # => [1, 2, nil] a, b, c, = 1, 2, 3, 4 # => [1, 2, 3] 自動(dòng)裁掉超出的長度
結(jié)合以上案例感覺 Java 對多重賦值不是很友好,很多不合規(guī)范的語法在編譯期就會被攔截并且報(bào)錯(cuò),簡單對比后總結(jié):
Java 因?yàn)閺?qiáng)類型,所以對賦值的比較限制多,例如只能對同類型的變量進(jìn)行簡單的賦值
Ruby 中多重賦值比較輕松,不用考慮類型,長度等問題,過長和過短都不會在編譯時(shí)拋出問題
Ruby 在聲明類型的時(shí)候不需要像 Java 那樣聲明類型,這也是動(dòng)態(tài)語言的特性,我個(gè)人是比較喜歡的
條件判斷
Ruby 的條件判斷主要有以下三種:
if 語句
unless 語句
case 語句
先看實(shí)例和對比代碼:
a, b = 10, 20 p "a 比 b 大" if a > b p "a 比 b 小" if a < b # => a 比 b 小 # unless 條件就不多做介紹,用法剛好與 if 語句相反,類似java中的 != 運(yùn)算符,實(shí)例代碼: a, b = 10, 20 p "a 和 b 不相等" unless a == b # => a 和 b 不相等
int a = 10, b = 20; if (a > b) System.out.println("a 比 b 大"); // 在合作項(xiàng)目中還是推薦使用中括號 {} if (a < b) System.out.println("a 比 b 小"); //=> a 比 b 小 int a = 10, b = 20; if (a != b) System.out.println("a 和 b 不相等"); //=> a 比 b 小
還有 case 語句主要用于多條件進(jìn)行判斷,語句用法是 case~when~end 進(jìn)行組合條件判斷,功能跟 Java 中的 switch 相同,還有邏輯運(yùn)算符 ==, !=, ||, && 都是通用的基本知識,所以就不寫詳細(xì)說明和寫示例代碼了,不然會顯得很啰嗦
總結(jié):條件判斷語句用法非常簡單,兩種編程語言基本類似語言類似,不過還是有以下區(qū)別:
Ruby 在關(guān)鍵字選擇上多一些,例如 unless 實(shí)際上是替代了運(yùn)算符 !=,也增加了一些可讀性
if 語法基本相似,但 Java 強(qiáng)制表達(dá)式必須使用括號 () ,Ruby則不需要
Ruby 使用 if~then~end 語法標(biāo)記代碼塊,不同于 Java 使用中括號 {} 標(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è)簡單的講解,對比兩種語言的使用區(qū)別,具體如下:
如何執(zhí)行一個(gè)固定次數(shù)的循環(huán) ?
如何遍歷一個(gè)數(shù)組 ?
如何遍歷一個(gè) Hash ?
執(zhí)行固定次數(shù)的循環(huán)是 time循環(huán) 方法的拿手好戲,用于和語句也很簡單,如果不需要下標(biāo)值,|i| 參數(shù)也是可以移除的,示例代碼如下
3.time do |i| # i 也可以省略 p "第#{i}次打印" end # => 第0次打印 # => 第1次打印 # => 第2次打印
在 Java 中想要執(zhí)行固定長度的循環(huán),不能通過 forEach只能通過古老的 for..i 來實(shí)現(xiàn),具體代碼如下:
for (int i = 0; i < 3; i++) { System.out.println("第" + i + "次打印"); } // 第0次打印 // 第1次打印 // 第2次打印
如何遍歷一個(gè)數(shù)組?
在 Ruby 中通常會推薦使用 **each ** 不僅語法簡單,而且可以輕松拿到元素值,示例代碼如下:
["abc","efg","hmn"].each do |e| p "#{e}!" end #=> abc! dfg! hmn!
Java 在 JDK 8 經(jīng)過 Stream 和 Lambda 語法增強(qiáng)后,遍歷數(shù)組也沒有想象中那么古板,示例代碼:
Arrays.asList("abc", "dfg","hmn").forEach(e -> System.out.println(e + "!") ); // abc! dfg! hmn!
不過在平時(shí)遍歷數(shù)組的時(shí)候經(jīng)常會遇到一種需求,不僅想要拿到數(shù)組的元素,還需要拿到當(dāng)前循環(huán)的索引值,Ruby 中提供一個(gè)特別的 each 方式實(shí)現(xiàn),就是 each_with_index 方法,它會把 [元素, 索引] 傳入到 do 代碼塊的后,具體示例代碼:
["abc","def","ghi"].each_with_index do |e, i| p "當(dāng)前元素 #{e} , 以及第 #{i} 次循環(huán)" end #=> "當(dāng)前元素 abc , 以及第 0 次循環(huán)" #=> ...
Java 想要實(shí)現(xiàn)相同循環(huán)效果就不能用基于迭代器的 ForEach 實(shí)現(xiàn)了,只能用 for..i 實(shí)現(xiàn),示例代碼如下:
List<String> list = Arrays.asList("abc", "deg", "ghi"); for (int i = 0; i < list.size(); i++) { String e = list.get(i); System.out.println("當(dāng)前元素" + e + ",以及第 " + i + "次循環(huán)"); } // 當(dāng)前元素abc,以及第 0次循環(huán) // ..
如何遍歷一個(gè) Hash ?
Hash 是 Ruby 的常用的對象,因此循環(huán)遍歷獲取 K,V 也是相當(dāng)方便的,示例代碼:
hash = {name: "apple", age: 15, phone: "15815801580"} hash.each do |k, v| p "key: #{k}, value: #{v}" end #=> key: name, value: apple #=> ...
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:
Map<String, String> hashMap = new HashMap<>(); hashMap.put("name", "apple"); hashMap.put("age", "15"); hashMap.put("phone", "15815801580"); for (Map.Entry<String, String> entry : hashMap.entrySet()) { System.out.println("key :" + entry.getKey() + ", value : " + entry.getValue()); } // key :name, value : apple // ....
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í)例對比:
// java 使用 while(true) 或者 for(;;) 實(shí)現(xiàn)無限循環(huán) while (true) System.out.println("i use java");
# ruby 無限循環(huán) loop do p "i use ruby" end
如果程序進(jìn)入無限循環(huán)就只能通過 CTRL + C 來終止程序運(yùn)行了 總結(jié):循環(huán)上兩種語言區(qū)別不大,Ruby 雖然循環(huán)方式多,但是平時(shí)常用的也就 each, for 會比較多,在循環(huán)上的區(qū)別,大多只是兩種語言在語法上的區(qū)別
方法
分類
Ruby 中的方法大致可分為 3 類:
實(shí)例方法
類方法
函數(shù)式方法
實(shí)例方法:Ruby 中的實(shí)例方法 Instance method 和 Java 中的普通方法類似,顧名思義就是調(diào)用方必須是一個(gè)類的實(shí)例(對象),需要調(diào)用實(shí)例方法就必須先通過類構(gòu)造一個(gè)實(shí)例對象才能進(jìn)行調(diào)用,具體請看示例代碼:
# ruby 中的實(shí)例方法 [1, 2, 3] .clear # 清理數(shù)組 => [] 100.to_s # int 轉(zhuǎn) string => "100" "100".to_i # string 轉(zhuǎn) int => 100 ["a", "b", "c"].index("b") # 查找下標(biāo) => result: 1
// java 中的實(shí)例方法 StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("abc"); // 實(shí)例方法 append stringBuilder.append("efg"); List<String> strList = new ArrayList<>(); strList.add("abc"); // 實(shí)例方法 add strList.add("efg");
類方法:Ruby 的類方法 class method 可以理解為 Java 的靜態(tài)方法,就是需要類對象作為接收方的方法,指無需構(gòu)建類的對象即可以直接通過類調(diào)用其自身的方法,大多常見于工具類當(dāng)中,請看示例代碼:
// java 中的靜態(tài)方法 Arrays.asList(T...a) // 數(shù)組轉(zhuǎn)集合 Executors.newCachedThreadPool() // 創(chuàng)建線程池
# ruby 中的類方法 Hash.new # 創(chuàng)建散列對象 Time.new # 創(chuàng)建時(shí)間對象
函數(shù)方法是指沒有接收者的方法,這種類型方法在Java中倒是不存在,參考示例代碼,例如上文中函數(shù)方法 p
p "hello" puts "print words"
定義實(shí)例方法
Ruby 定義方法非常簡單,沒有 Java 那么多的格式規(guī)范:修飾符:靜態(tài)聲明:返回值:方法名:(參數(shù)...),在方法聲明的形式上要簡單許多,主要通過 def 關(guān)鍵字定義,具體參考示例代碼:
// java define method public static int add(int x, int y) { return x * y } add(2, 5) // 10
# ruby define method def add(x, y) x * y end add(2, 5) #=> 10 # 帶默認(rèn)值的方法 def add(x=2, y=6) x * y end # 省略參數(shù)使用默認(rèn)值調(diào)用方法 add #=> 12 # 指定參數(shù)的方法 add(2, 5) #=> 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á)式會成為方法的返回值遵循這個(gè)約定所以上述的方法就可以省略 return 關(guān)鍵字,所以在日常開發(fā)中會較少的使用 return 關(guān)鍵字
定義類方法
前面講過 Ruby 的類方法實(shí)際上就等于 Java 的靜態(tài)方法,Ruby 中定義類方法的示例代碼:
# ruby 定義類方法 class Hello # class << self 定義類方法的一種方式 class << self def say_morning p "hello good morning" end #... class << self 塊后面還可以定義多個(gè)類方法 end end Hello.say_morning # 類方法 #=> "hello good morning" h = Hello.new h.say_morning # 如果用實(shí)例對象類方法就會報(bào)錯(cuò)!undefined method 'say_morning'
在 Java 中定義靜態(tài)方法的示例:
public class Hello { public static void sayMorning() { System.out.println("hello good morning"); } public static void main(String[] args) { Hello.sayMorning(); } } #=> "hello good morning"
定義方法也都是很簡單的知識點(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ù)的示例代碼:
// 方法要求類型,順序,并且必傳 public void show(String name, int age, String address) { System.out.println(name + ", " + age + ", " + address); } // new Person().show("胖大海"); // 編譯出錯(cuò),參數(shù)不符合要求 // new Person().show("胖大海", 19); // 編譯出錯(cuò),參數(shù)不符合要求 new Person().show("胖大海", 19, "北京朝陽區(qū)"); // 編譯通過輸出:胖大海, 19, 北京朝陽區(qū)
Ruby 則會靈活一些,具體請看示例代碼:
# 無需聲明類型,帶默認(rèn)值的參數(shù)可不傳 def show(name, age=18, address="北京市") p "#{name}, #{age}, #{address}" end Person.new.show("胖胖") #=> 輸出:胖胖, 18, 北京市
不過對于可變參數(shù)兩種語言的實(shí)現(xiàn)幾乎相同,形式上都是對參數(shù)進(jìn)行特殊標(biāo)記,Java 是通過在參數(shù)前面加...標(biāo)識,ruby 則在參數(shù)前面使用 * 號標(biāo)識,解釋器會對這種語法用數(shù)組進(jìn)行轉(zhuǎn)換,兩者代碼量也差不多,沒有什么差別,簡單看下示例代碼:
public void names(String ...names) { System.out.println("params :" + Arrays.toString(names)); } new Person().names("java", "ruby", "go", "c++"); // 輸出結(jié)果 :params :[java, ruby, go, c++]
def names(*names) p "params: #{names}" end Person.new.names("java", "ruby", "go", "c++") # 輸出結(jié)果:params: [\"java\", \"ruby\", \"go\", \"c++\"]
簡單通過 2 段代碼的對比,我們可以對兩種語言的方法參數(shù)得出以下結(jié)論:
Java 方法會嚴(yán)格按照定義,強(qiáng)制要求類型,值必傳,否則編譯期會報(bào)錯(cuò),并且無法在聲明時(shí)定義參數(shù)的默認(rèn)值
Ruby 方法參數(shù)未設(shè)定默認(rèn)值,不傳參數(shù),只會在執(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)鍵字定義類,簡單的用法參考以下代碼:
class Hello end h = Hello.new
Java 也是通過 class 定義類,不同的是最外層的類 Java 必須使用 public 進(jìn)行修飾,具體請看示例代碼:
public class Hello { public static void main(String[] args) { Hello h = new Hello(); } }
那么 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ù)上對比的示例代碼:
# 帶構(gòu)造函數(shù)的 Ruby 類 class Hello def initialize(name="Ruby") # 默認(rèn)參數(shù) @name = name end def say_hello # 實(shí)例方法 p "hello #{@name} ! how are you?" end end h = Hello.new("james") h.say_hello #=> "hello james ! how are you?" // 帶構(gòu)造函數(shù)的 java 類 public class Hello { private String name; // 有參構(gòu)造函數(shù) public Hello(String youName) { this.name = youName; } public void sayHello() { System.out.println("hello! " +name+ " how are you ?"); } public static void main(String[] args) { Hello h = new Hello("jack"); h.sayHello(); } } #=> "hello! jack how are you ?"
方法聊到這里,下來聊聊方法里的常量
常量對比
如果在 Java 和 Ruby 中定義常量,參考示例代碼:
// Java 中定義常量 public class Hello { // 常量必須是 static final 修飾,代表不可變 public static final String VERSION = "1.0"; public static void main(String[] args) { // Hello.VERSION = "1.5"; // 嘗試修改則會在編譯期報(bào)錯(cuò) System.out.println(Hello.VERSION); } } #=>"1.0" # Ruby 中定義常量 class PhoneNumber BeiJing = "020" GuangZhou = "021" end p PhoneNumber::BeiJing #=> "020" p PhoneNumber::GuangZhou #=> "021" Phonenumber::BeijING = "303" #=> Ruby 可以修改常量,不會報(bào)錯(cuò),但會提示警告 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)作會讓編譯器報(bào)錯(cuò) Connot assign a value to final variable 并且無法通過編譯,Ruby 則不同,允許修改常量,但解釋器會提示警告信息:warning: already initialized constant
訪問級別
Ruby 和 Java 在方法訪問級別上沒有什么很大不同,只是 Ruby 沒有包(Package)的概念,所有自然也就沒有 Java 里面的包訪問權(quán)限,細(xì)節(jié)上但是還有些許區(qū)別,Ruby 的三種訪問級別的定義方法,具體用法直接看示例代碼:
# 定義方法時(shí)聲明訪問權(quán)限 private def call_v1 p "call_v1 is private" end # 定義方法后,再設(shè)定訪問權(quán)限 def call_v2 p "call_v2 is private" end private :call_v2 # 設(shè)定 call_v2 為 private # 對代碼塊設(shè)定, 以下的方法定義為 private private def call_v3 p "call_v3 is private" end def call_v3 p "call_v4 is private" end
Java 設(shè)定方法訪問級別的方式則比較單一,只能在定義時(shí)聲明:
private String priv() { return "priv is private"; }
綜上所述,兩種語言在訪問級別的差異和總結(jié):
Java 方法默認(rèn)修飾符是 包訪問權(quán)限
Ruby 方法默認(rèn)訪問級別是 public(initialize 例外)
Java 方法只能在定義的時(shí)候使用關(guān)鍵字設(shè)定訪問級別
Ruby 常用的則有三種方式可以設(shè)定方法的訪問級別,非常靈活
繼承
Ruby 和 Java 的所有類都是基于 Object 的子類,Ruby 則還有更加輕量級的 BasicObject原始類,這里先不詳細(xì)描述,繼承這個(gè)概念也不多說,面向?qū)ο蟮幕A(chǔ)知識,直接先看兩種語言實(shí)現(xiàn)繼承的方式
Ruby 通過 < 父類名 實(shí)現(xiàn)繼承,示例代碼:
class Car def drive p "car start.." end end class SuperCar < Car def speed_up p "speed to 200km ..." end end s = SuperCar.new s.drive s.speed_up #=> "car start.." #=> "speed to 200km ..."
Java 通過 extends實(shí)現(xiàn)繼承的,示例代碼:
class Car { void drive(){ System.out.println("Car Start ..."); } } public class SuperCar extends Car { void speedUp() { System.out.println("speed to 200km..."); } public static void main(String[] args) { SuperCar c = new SuperCar(); c.drive(); c.speedUp(); } } // Car Start ... // 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)建模塊
module Display def open p "open display..." end end 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 思想對現(xiàn)實(shí)進(jìn)行抽象的時(shí)候,會發(fā)現(xiàn)很多非繼承關(guān)系又存在共同功能的事物,例如智能手機(jī),手表繼承自不同的父類,但是他們又都有看時(shí)間 watch_time的能力,我們用代碼展現(xiàn)這種繼承關(guān)系,請看示例代碼:
class Phone def call p "phone call.." end end class IPhone < Phone # iPhone 繼承自 Phone 類,但是不僅可以打電話,還可以看時(shí)間 def watch_time p "watch_time 12:00:000" end end class Watch # 手表都可以看時(shí)間 def watch_time p "watch_time 12:00:000" end end class AppleWatch < Watch # AppleWatch 不僅看時(shí)間,還有運(yùn)動(dòng)的功能 def run p "start run" end end
結(jié)合上面的代碼,我們可以看到 watch_time 代碼重復(fù),對于不同繼承體系但是相同功能的時(shí)候,就可以用 Mix-in 解決這個(gè)問題,思路如下:
將例如 watch_time 相同的方法和代碼,抽出定義在 module 模塊中
使用 include 引入模塊,將方法引入到實(shí)際的類中
使用 Mix-in 后我們可以看下代碼變化,示例代碼:
module WatchTime # 抽出通用的方法 def watch_time p "watch_time 12:00:000" end end class Phone def call p "phone call.." end end class IPhone < Phone include WatchTime end class Watch include WatchTime end class Apple_watch < Watch # apple_watch 不僅看時(shí)間,還可以運(yùn)動(dòng) def run p "start run" end 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è)一張我們在業(yè)務(wù)代碼中經(jīng)常遇到的情況,根據(jù)表達(dá)式取值,當(dāng)表達(dá)式為 true 時(shí)改變變量的值,這種簡單邏輯賦值在 Java 只能這樣寫,請看示例代碼
String value = "abc"; if (condition != null) { value = condition; } // 看上去很啰嗦
這種情況在 Ruby 中一行代碼可以實(shí)現(xiàn)相同語義:
# 當(dāng) condition 表達(dá)式為 true 執(zhí)行 value = condition , 否則執(zhí)行 value = "abc" value = condition || "abc"
只所以可以實(shí)現(xiàn)是因?yàn)?Ruby 有一個(gè)不同 Java 的特定, Ruby 對象都可以用于進(jìn)行布爾表達(dá)式判斷,判斷邏輯為**對象本身不為 nil 或者 false 表達(dá)式則為 true,否則為 false **
還有一種邏輯則是取相反的情況,例如我們經(jīng)常遇到一種情況是,判斷數(shù)組不為空的時(shí)候取數(shù)組的某一個(gè)下標(biāo),在 Java 中只能這樣寫,示例代碼
List<String> list = Arrays.asList("a", "b", "c"); String item = null; if (list != null) { item = list.get(0); } // "a"
這種情況可以用邏輯運(yùn)算符 &&, 它剛好與上面 || 相反,也是一行代碼可以實(shí)現(xiàn)相同功能
str_list = ["a", "b", "c"] item = str_list && str_list[0] #=> "a"
我個(gè)人非常喜歡這種簡潔的寫法,不過建議在多人項(xiàng)目中不要用太多語法糖,不然可能會造成項(xiàng)目代碼可讀性混亂
異常處理
很多程序員大部分時(shí)間都花在查錯(cuò)上,所以迅速定位異常很關(guān)鍵,先看看 Ruby 的異常格式 文件名:行號: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 沒有的特性,它適合對一些容易出錯(cuò)的程序(例如調(diào)用外部 API )可以進(jìn)行快速重試,具體可以請看示例代碼
class HandleException def zero x, y = 100, 0 begin x = x / y rescue p '執(zhí)行異常邏輯' y = 2 retry end x end end h = HandleException.new p h.zero #=>"執(zhí)行異常邏輯" #=>50
上述程序非常簡單,大概邏輯是首次執(zhí)行會拋出異常,然后被 rescue 捕獲后重新復(fù)制,第二次運(yùn)算成功,Java 如果要實(shí)現(xiàn)相同的語義的話,則代碼沒有這么簡潔了,跟上章節(jié)的邏輯運(yùn)算符 &&,|| 類似 resuce 也具備條件表達(dá)式的運(yùn)算,具備很強(qiáng)的表達(dá)能力,我們嘗試對以上述代碼進(jìn)行一些簡化,示例代碼:
x, y = 100, 0 x = x / y rescue 50 # => 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 里面就會顯得有點(diǎn)啰嗦,請看示例代碼:
int x = 100, y = 0; try { x = x / y; }catch (ArithmeticException ae) { x = 50; } // 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 異常或其子類
Java 異常都是繼承 Throwable ,異常被劃分為 Error 異常和 Exception,程序通常只能處理 Exception 的子類 RuntimeException 以及其子類
Ruby 支持 retry 從異常中快速重試,rescue 表達(dá)式簡化異常代碼處理,Java 則沒有該功能
Java 主動(dòng)拋異常的使用 throw new Exception,而 Ruby 則使用 raise 方法
兩種語言的基本語法對比就先寫到這里,暫時(shí)就不寫分析和總結(jié)了,因?yàn)槲液罄m(xù)還會繼續(xù)探索 Ruby 和 Java 在其他使用層面的使用區(qū)別對比,例如字符串,數(shù)據(jù)類型,集合,哈希,最后想留一個(gè)問題:你覺得靜態(tài)語言和動(dòng)態(tài)語言最明顯的區(qū)別在哪里?他們各自會存在什么問題?在什么場景下你會偏向動(dòng)態(tài)語言,什么場景你會偏向靜態(tài)語言?
到此,相信大家對“Ruby和Java的基礎(chǔ)語法有哪些區(qū)別”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。