- 一、什麼是Groovy
- Groovy 是 JVM 的一個替代語言 — 替代 是指可以用 Groovy 在 Java 平臺上進行 Java 編程,使用方式基本與使用 Java 代碼的方式相同。注意:不是指Groovy替代java,而是指Groovy和java很好的結合編程
- ● 是一個基於 Java虛擬機的敏捷 動態語言。
- ● 構建在強大的Java語言之上 並 添加了從Python,Ruby和Smalltalk等語言中學到的 諸多特徵。
- ● 爲Java開發者提供了 現代最流行的編程語言特性,而且學習成本很低(幾乎爲零)。
- ● 支持DSL(Domain Specific Languages領域定義語言)和其它簡潔的語法,讓你的代碼變得易於閱讀和維護。
- ● Groovy擁有處理原生類型,面向對象以及一個Ant DSL,使得創建Shell Scripts變的非常簡單。
- ● 在開發Web,GUI,數據庫或控制檯程序時 通過 減少框架性代碼 大大提高了開發者的效率。
- ● 支持單元測試和模擬(對象),可以 簡化測試。
- ● 無縫集成 所有已經存在的 Java對象和類庫。
- ● 直接編譯成Java字節碼,這樣可以在任何使用Java的地方 使用Groovy。
- Groovy 的一個好處是,它的語法與 Java 語言的語法很相似。雖然 Groovy 的語法源於 Smalltalk 和 Ruby 這類語言的理念,但是可以將它想像成 Java 語言的一種更加簡單、表達能力更強的變體。(在這點上,Ruby 與 Groovy 不同,因爲它的語法與 Java 語法差異很大。)
- 許多 Java 開發人員非常喜歡 Groovy 代碼和 Java 代碼的相似性。從學習的角度看,如果知道如何編寫 Java 代碼,那就已經瞭解 Groovy 了。Groovy 和 Java 語言的主要區別是:完成同樣的任務所需的 Groovy 代碼比 Java 代碼更少。(有時候會少很多!)
- 二、開發環境
- 1. jdk1.6
- 2. eclipse+groovy plugin(3.7)
- 打開eclipse,通過 Help->Install New Software,下載並安裝groovy插件。 New一個遠程站點, url可以使用 groovy - http://dist.codehaus.org/groovy/distributions/greclipse/snapshot/e3.7/,選擇安裝groovy插件。
- 三、新建Groovy項目
- 1、新建Groovy項目
- 2、添加Groovy Libraries
- 3、添加Groovy Class,自動生成Main方法,在方法中添加一句代碼“println "Hello World"”,右鍵運行,在控制檯上輸出“Hello World”。
- 四、Groovy語法簡介
- 1、沒有類型的Java
- 作爲動態語言,groovy中所有的變量都是對象(類似.net framework, 所有對象繼承自java.lang.Object),在聲明一個變量時,groovy不要求強制類型聲明,僅僅要求變量名前使用關鍵字def。
- 修改main方法中和代碼:
- def var="hello world"
- println var
- println var.class
- 就可以看到程序最後輸出了var的實際類型爲java.lang.String
- 作爲例外,方法參數和循環變量的不需要def。
- 2、不需要的public
- 你可以把main方法前面的public去掉,實際上groovy中默認的修飾符就是public所以public修飾符根本就不需要寫,這點和java不一樣。
- 3、不需要的語句結束符
- Groovy中沒有語句結束符,當然爲了和java保持一致,也可以使用;號作爲語句結束符。在前面的每一句代碼後面加上;號結束,程序同樣正常運行(爲了接受java程序員的頑固習慣)。
- 4、字符串連接符
- 跟java一樣,如果需要把一個字符串寫在多行裏,可以使用+號連接字符串,代碼可以這樣寫:
- def var = "hello " +
- "world" +
- ",groovy"
- 當然更可以這樣寫
- def var = """hello
- world
- groovy!"""
- 三個"號之間不再需要+號進行連接(不過字符串中的格式都會被保留,包括回車和tab)。
- 5、一切皆對象
- 聽起來像是“衆生平等”的味道,事實上groovy對於對象是什麼類型並不關心,一個變量的類型在運行中隨時可以改變,一切根據需要而定。如果你賦值給它boolean,那麼不管它原來是什麼類型,它接受boolean值之後就會自動把類型轉變爲boolean值。看下面的代碼:
- def var = "hello " +
- "world" +
- ",groovy"
- println var
- println var.class
- var=1001
- println var
- println var.class
- var=false
- println var
- println var.class
- 輸出結果是
- hello world,groovy
- class java.lang.String
- 1001
- class java.lang.Integer
- false
- class java.lang.Boolean
- var這個變量在程序運行中,類型在改變。一開始給它賦值String,它的類型就是String,後來給它賦值Integer,它的類型就是Integer,最後給它賦值Boolean,它的類型就是Boolean。
- 6、循環
- 刪除源文件下的所有內容,用一下內容代替
- static void main(String[] args) {
- def var = "hello " +
- "world" +
- ",groovy"
- repeat(var)
- }
- def static repeat(val) {
- for(i=0;i<5;i++) {
- println val
- }
- }
- 輸出結果是:
- hello world,groovy
- hello world,groovy
- hello world,groovy
- hello world,groovy
- hello world,groovy
- 注意循環變量i前面沒有def。當然也沒有java中常見的int,但是如果你非要加上int也不會有錯,因爲Groovy 1.1beta2之後開始(不包含1.1beta2),groovy開始支持java經典的for循環寫法。
- 此外,上面的for語句還可以寫成:
- for(i in 0..<5)
- 這樣的結果是一樣的。
- 7、String和Gstring
- 除了標準的java.lang.String以外,groovy還支持Gstring字符類型。把上面的for循環中的語句換成:
- println "This is ${i}: ${val}"
- 輸出:
- This is 0: hello world,groovy
- This is 1: hello world,groovy
- This is 2: hello world,groovy
- This is 3: hello world,groovy
- This is 4: hello world,groovy
- 8、範圍
- 在前面的for循環介紹中我們已經使用過的for(i in 0..5) 這樣的用法,其實就是一個範圍。
- 範圍是一系列值。例如“0..4”表示包含整數0、1、2、3、4.Groovy還支持排除範圍,“0..<4”表示0、1、2、3.還可以創建字符範圍:“a..e”相當於a、b、c、d、e。“a..<e”包括小於e的值。
- 9、默認參數值
- 還可以爲方法指定默認參數值。修改repeat方法的定義:
- def static repeat(val, repeatNum=3) {
- for(i in 0..<repeatNum) {
- println "This is ${i}: ${val}"
- }
- }
- 可以看到,repeat方法增加了一個參數repeatNum(並給了一個默認值3),用於指定循環次數。當我們不指定第二個參數調用repeat方法時,repeatNum參數取默認值。
- 10、集合
- Groovy支持最常見的兩個java集合:java.util.Collection和java.util.Map。前面所說的範圍實際也是一種(java.util.List)。
- (1)Collection
- Groovy中這樣來定義一處collection:
- def collect=["a","b","c"]
- 除了聲明時往集合中添加元素外,還可以用以下方式向集合中添加元素:
- collect.add(1);
- collect<<"Come on"
- collect[collect.size()]=100.0
- Collection使用類似數組下標的方式進行檢索:
- collect[collect.size()-1]
- collect[5]
- groovy支持負索引:
- println collect[-1] //索引倒數第1個元素
- println collect[-2] //索引倒數第2個元素
- Collection支持集合運算:
- collect=collect+5 //在集合中添加元素5
- println collect[-1] //索引倒數第1個元素
- collect=collect-"a" //在集合中減去元素a(第1個)
- println collect[0] //索引集合中第1個元素(現在是b)
- 同樣地,可以往集合中添加另一個集合或刪除一個集合:
- collect=collect-collect[0..4] //把集合中的前5個元素去掉
- println collect[0] //現在集合中只有1個元素,即原來最後一個
- println collect[-1] //也可以用負索引,證明最後一個元素就是第一個元素
- (2)Map
- Map是“鍵-值”對的集合,在groovy中,鍵不一定是String,可以是任何對象(實際上Groovy中的Map就是java.util.LinkedHashMap)。
- 如此可以定義一個Map:
- def map=['name':'john','age':14,'sex':'boy']
- 添加項:
- map=map+['weight':25] //添加john的體重
- map.put('hight', 1.27) //添加john的身高
- map.father='Keller' //添加john的父親
- 可以用兩種方式檢索值:
- println map['father'] //通過key索引下標索引
- println map.hight //通過key作爲成員名索引
- 11、閉包(Closure)
- 閉包是用{符號括起來的代碼塊,它可以被單獨運行或調用,也可以被命名。類似‘匿名類’或內聯函數的概念。
- 閉包中最常見的應用是對集合進行迭代,下面定義了閉包對map進行了迭代:
- map.each ({key,value->println "$key:$value"}) //key,value兩個參數用於接受每個元素
- map.each {println it} //it是一個關鍵字,代表map集合的每一個元素
- 除了用於迭代之外,閉包可以單獨定義:
- def say={word-> println "Hi, $word!"}
- 調用:
- say("groovy")
- say.call("groovy & java")
- 輸出:
- Hi, groovy!
- Hi, groovy & java!
- 看起來,閉包類似於方法,需要定義參數和要執行的語句,它也可以通過名稱被調用。然而閉包對象(不要奇怪,閉包也是對象)可以作爲參數傳遞(比如前面的閉包作爲參數傳遞給map的each方法)。而在java中,要做到這一點並容易(也許c++的函數指針可以,但不要忘記java中沒有指針)。其次,閉包也可以不命名(作爲代價,不能在定義閉包時執行一次),而方法不可以。
- 12、類
- Groovy類的java類一樣,你完全可以用標準java bean的語法定義一個Groovy類。但作爲另一種語言,我們可以使用更Groovy的方式定義的使用類,這樣的好處是,你可以少寫一半以上的javabean代碼。
- (1)不需public修飾符
- 如前面所言,Groovy的默認訪問修飾符就是public,如果你的Groovy類成員需要public修飾,則你根本不用寫它。
- (2)不需要類型說明
- 同樣前面也說過,Groovy也不關心變量和方法參數的具體類型。
- (3)不需要getter/setter方法
- 不要奇怪,在很多ide(如eclipse)早就可以爲程序員自動產生getter/setter方法了。在Groovy中,則徹底不需要getter/setter方法--所有類成員(如果是默認的public)根本不用通過getter/setter方法引用它們(當然,如果你一定要通過getter/setter方法訪問成員屬性,Groovy也提供了它們)。
- (4)不需要構造函數
- 不再需要程序員聲明任何構造函數,因爲實際上只需要兩個構造函數(1個不帶參數的默認構造函數,1個只帶一個map參數的構造函數--由於是map類型,通過這個參數你可以構造對象時任意初始化它的成員變量)。
- (5)不需要return
- Groovy中,方法不需要return來返回值嗎?這個似乎很難理解,看後面的代碼吧,因此,Groovy風格的類是這樣的
- (6)不需要()
- Groovy中方法調用 可以省略()(構造函數除外),也就是說下面兩名是等同的:
- def person1 = new Person()
- person1.setName "john"
- person1.setAge(25)
- 下面看一個完整類定義的例子:
- class Person {
- def name
- def age
- String toString() { //注意方法是String,因爲我們需要覆蓋的方法爲String類型
- "$name, $age"
- }
- 如果你使用javabean風格來做同樣的事,起碼代碼量要增加1倍以上。
- 我們可以使用默認構造方法實例化Person類:
- def person1 = new Person()
- person1.setName "john"
- person1.setAge(25)
- 也可以用Groovy的風格做同樣的事:
- def person2=new Person(['name':'john','age':25])
- println person2
- 這樣需要注意我們覆蓋了Object的toString方法,因爲我們想通過println person1這樣的方法簡單地打印對象的屬性值。
- 然而toString方法中並沒有return一個String,但不用擔心,Groovy默返回方法的最後一行的值。
- 13、?運算符
- 在java中,有時候爲了避免出現空指針異常,我們通常需要這樣的技巧:
- if(rs!=null) {
- rs.next()
- ......
- }
- 在Groovy中,可以使用?操作符達到同樣的目的:
- rs?.next()
- ?在這裏是一個條件運算符,如果?前面的對象非null,執行後面的方法,否則什麼也不做。
- 14、可變參數
- 等同於java5中的變長參數。首先我們定義一個變長參數的方法sum:
- int sum(int... var) {
- def total=0
- for(i in var) {
- total+=1
- }
- return total
- }
- 我們可以調用sum時使用任意個的參數(1個,2個,3個……):
- println sum(1)
- println sum(1,2)
- println sum(1,2,3)
- 15、枚舉
- 定義一個enum:
- def today=Day.SATURDAY
- switch(today) {
- //Saturday or Sunday
- case [Day.SATURDAY, Day.SUNDAY]:
- println "Weekends are cool"
- break;
- case Day.MONDAY..Day.FRIDAY:
- println "Boring work day"
- break;
- default:
- println "Are you sure this is a valid day?"
- }
- 注意,switch和case中可以使用任何對象,尤其是可以在case中使用List和範圍,從而使分支滿足多個條件。
- 同java5一樣,Groovy支持帶構造器。屬性的方法的enum:
- enum Planet {
- MERCURY(3.303e+23,2.4397e6),
- VENUS(4.869e+24,6.0518e6),
- EARTH(5.976e+24,6.37814e6),
- MARS(6.421e+23,3.3972e6),
- JUPITER(1.9e+27,7.1492e7),
- SATURN(5.688e+26,6.0268e7),
- URANUS(8.686e+25,2.5559e7),
- NEPTUNE(1.024e+26,2.4746e7)
- double mass
- double radius
- Planet(double mass, double radius) {
- this.mass=mass
- this.radius=radius
- }
- void printMe(){
- println :"${name()} has a mass of ${mass} and a radius of ${radius}"
- }
- }
- Planet.EARTH.printMe()
- 16、Elvis運算符
- 這是三目運算符“?:”的簡單形式,三目運算符通常以這種形式初戀:
- String displayName=name!=null?name:"Unknown";
- 在Groovy中,也可以簡化爲(因爲null在Groovy中可以轉化爲布爾值false):
- String displayName=name?name:"Unknown";
- 基於“不重複”的原則,可以是用elvis操作符再次簡化爲:
- String displayName=name?:"Unknown";
- 17、動態性
- Groovy所有的對象都有一個元素metaClass,我們可以通過metaClass屬性訪問該元類。通過元類,可以爲這個對象增加方法(在java中不可想象!)見下面的代碼,msg是一個String,通過元類,我們爲msg增加了一個String類中沒有的方法up:
- def msg="Hello!"
- println msg
- println msg.metaClass
- String.metaClass.up={delegate.toUpperCase()}
- println msg.up()
- 通過元類,我們還可以檢索對象所擁有的方法和屬性(就是反射):
- msg.metaClass.metaMethods.each {println it.name}
- msg.metaClass.properties.each { println it.name}
- 甚至我們可以看到我們剛纔添加的up方法。
- 我們可以通過元類判斷有沒有一個叫up的方法,然後再調用它:
- if (msg.metaClass.respondsTo(msg, 'up')) {
- println msg.up()
- }
- 當然,也可以推斷它有沒有一個叫bytes的屬性:
- if(msg.metaClass.hasProperty(msg, 'bytes')) {
- println msg.bytes.encodeBase64()
- }