轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/70313790
本文出自【趙彥軍的博客】
概念
Groovy是一種動態語言,它和Java類似(算是Java的升級版,但是又具備腳本語言的特點),都在Java虛擬機中運行。當運行Groovy腳本時它會先被編譯成Java類字節碼,然后通過JVM虛擬機執行這個Java字節碼類。
Groovy 配置環境變量
-
在 Groovy 官網下載壓縮包 http://www.groovy-lang.org/download.html
-
然后解壓到本地,如圖所示:
- 在
Path
環境變量中添加 Groovy 的bin 目錄路徑,比如:
D:\soft\apache-groovy-sdk-2.4.10\groovy-2.4.10\bin
如圖所示:
- 用 CMD 打開命令行,執行:
groovy -version
, 如果看到如下提示,就代表配置成功了。
Groovy Version: 2.4.10 JVM: 1.8.0_112 Vendor: Oracle Corporation OS: Windows 10
如圖所示:
開發工具 IntelliJ IDEA
groovy 的開發工具是 IntelliJ IDEA
下載地址: https://www.jetbrains.com/idea/
安裝完成后,新建項目 ,項目類型選擇 Groovy ,然后填寫 JDK 、Groovy 的安裝目錄
新建的項目 Groovy 如下圖所示:
在 src 目錄下,新建包名 groovy , 並且創建 groovy 源文件 Test.groovy,如下圖所示:
現在我們在 Test.groovy 中輸出一句 helloword ,代碼如下:
package groovy println( "hello world")
運行 Test.groovy 文件 ,如下圖所示:
Groovy 語法
基本語法
1、Groovy注釋標記和Java一樣,支持 //或者/**/
2、Groovy語句可以不用分號結尾。Groovy為了盡量減少代碼的輸入,確實煞費苦心
3、單引號
單引號” 中的內容嚴格對應Java中的String,不對 $ 符號進行轉義
def s1 = 'i am 100 $ dolloar' println( s1 )
運行結果:
i am 100 $ dolloar
4、雙引號
雙引號”“的內容則和腳本語言的處理有點像,如果字符中有號的話,則它會表達式先求值。
def x = 100 def s1 = "i am $x dolloar" println( s1 )
運行結果:
i am 100 dolloar
5、三引號
三個引號”’xxx”’中的字符串支持隨意換行 比如
def s1 = ''' x
y
z
f
''' println(s1)
運行結果:
x
y
z
f
定義變量
Groovy中支持動態類型,即定義變量的時候可以不指定其類型。Groovy中,變量定義可以使用關鍵字def。注意,雖然def不是必須的,但是為了代碼清晰,建議還是使用def關鍵字
- 定義一個變量
def a = 1 //定義一個整形 def b = "字符串" //定義一個字符串 def double c = 1.0 //定義一個 double 類型,也可以指定變量類型
定義函數
無返回類型的函數定義,必須使用def關鍵字 ,最后一行代碼的執行結果就是本函數的返回值
//無參函數 def fun1(){ } //有參函數 , 無需指定參數類型 def fun2( def1 , def2 ){ }
如果指定了函數返回類型,則可不必加def關鍵字來定義函數
String fun3(){ return "返回值" }
其實,所謂的無返回類型的函數,我估計內部都是按返回Object類型來處理的。畢竟,Groovy 是基於Java 的,而且最終會轉成 Java Code 運行在 JVM 上 .
Groovy的函數里,可以不使用return xxx 來設置 xxx 為函數返回值。如果不使用 return 語句的話,則函數里最后一句代碼的執行結果被設置成返回值。
def getSomething(){ "getSomething return value" //如果這是最后一行代碼,則返回類型為String 1000 //如果這是最后一行代碼,則返回類型為Integer }
除了每行代碼不用加分號外,Groovy中函數調用的時候還可以不加括號
例子1:
def s1 = "123" println(s1) //或者 println s1
例子2:
println(fun1()) println fun1() def fun1(){ "你好" }
效果:
你好
你好
斷言 assert
- 斷言變量為空
def s1 = null assert s1
效果如下:
- 斷言變量的長度
def s2 = "abc" assert s2.size()>3
- 1
- 2
- 3
效果如下:
如果斷言發生,斷言后面的代碼無法執行
循環 for
- 方式一
for (i = 0; i < 5 ; i++) { println("測試") }
1、運行結果: 輸出5個測試
2、groovy 語法中,注意 i 前面不用指定 int 類型。
- 方式二
for (i in 0..5){ println("hello world") }
這種方式也是一種循環,只不過他輸出的是 6 個 hello world
, 如果想要輸出5個,有3中方式。
第一種方法
for (i in 0..<5){ println("hello world") }
第二種方法
for (i in 0..4){ println("hello world") }
第三種方法
for (i in 1..5){ println("hello world") }
循環 time
times表示從0開始循環到4結束
4.times { println it }
結果:
0
1
2
3
三目運算符
Java 語法
def name def result = name != null ? name : "abc" println(result)
groovy 語法
def name = 'd' def result = name?: "abc"
捕獲異常
捕獲所有的 Exception ,有兩種寫法
//第一種寫法,Java 寫法 try { println 5 / 0 } catch (Exception e) { } //第二種寫法,Groovy 寫法 try { println 5 / 0 } catch (anything) { }
這里的any並不包括Throwable,如果你真想捕獲everything,你必須明確的標明你想捕獲Throwable
switch
age = 36 def rate switch (age) { case 10..26: rate = 0.05 break case 27..36: rate = 0.06 break case 37..46: rate = 0.07 break default: throw new IllegalArgumentException() } println( rate)
判斷是否為真
Person person
//Java 寫法 if (person!= null){ if (person.Data!=null){ println person.Data.name } } //Groovy println person?.Data?.name
asType
asType 就是數據類型轉換
//String 轉成 int def s2 = s1 as int //String 轉成 int def s3 = s1.asType(Integer)
Groovy 數據類型
Groovy中的數據類型主要分2種
-
一個是Java中的基本數據類型。
-
另外一個是Groovy中的容器類。
-
最后一個非常重要的是閉包。
Java 基本類型
def boolean s1 = true def int s2 = 100 def String s3 = "hello world" if (s1) { println("hello world") }
Groovy 容器
List:鏈表,其底層對應Java中的List接口,一般用ArrayList作為真正的實現類。
Map:鍵-值表,其底層對應Java中的LinkedHashMap。
Range:范圍,它其實是List的一種拓展。
- List
//變量定義:List變量由[]定義,其元素可以是任何對象 def aList = [5,'string',false] println(aList) println aList[0] //獲取第1個數據 println aList[1] //獲取第2個數據 println aList[2] //獲取第3個數據 println aList[3] //獲取第4個數據 println( "集合長度:" + aList.size()) //賦值 aList[10] = 100 //給第10個值賦值 aList<<10 //在 aList 里面添加數據 println aList println "集合長度:" + aList.size()
效果如下:
[5, string, false] 5 string false null 集合長度:3 [5, string, false, null, null, null, null, null, null, null, 100] 集合長度:11
- map
def map = [key1: "value1", key2: "value2", key3: "value3"] println map //[key1:value1, key2:value2, key3:value3] println("數據長度:" + map.size()) //數據長度:3 println(map.keySet()) //[key1, key2, key3] println(map.values()) //[value1, value2, value3] println("key1的值:" + map.key1) //key1的值:value1 println("key1的值:" + map.get("key1")) //key1的值:value1 //賦值 map.put("key4", "value4") Iterator it = map.iterator() while (it.hasNext()) { println "遍歷map: " + it.next() } //遍歷map: key1=value1 //遍歷map: key2=value2 //遍歷map: key3=value3 //遍歷map: key4=value4 map.containsKey("key1") //判斷map是否包含某個key map.containsValue("values1") //判斷map是否包含某個values map.clear() //清除map里面的內容 Set set = map.keySet(); //把 map 的key值轉換為 set
- range
Range 是 Groovy 對 List 的一種拓展
def range = 1..5 println(range) //[1, 2, 3, 4, 5] range.size() //長度 range.iterator() //迭代器 def s1 = range.get(1) //獲取標號為1的元素 range.contains( 5) //是否包含元素5 range.last() //最后一個元素 range.remove(1) //移除標號為1的元素 range.clear() //清空列表
例子2:
def range = 1..5 println(range) //[1, 2, 3, 4, 5] println("第一個數據: "+range.from) //第一個數據 //第一個數據: 1 println("最后一個數據: "+range.to) //最后一個數據 //最后一個數據: 5
閉包
閉包,英文叫Closure,是Groovy中非常重要的一個數據類型或者說一種概念了。閉包,是一種數據類型,它代表了一段可執行的代碼。
def aClosure = {//閉包是一段代碼,所以需要用花括號括起來.. String param1, int param2 -> //這個箭頭很關鍵。箭頭前面是參數定義,箭頭后面是代碼 println"this is code" //這是代碼,最后一句是返回值, //也可以使用return,和Groovy中普通函數一樣 }
簡而言之,Closure的定義格式是:
def xxx = {paramters -> code} //或者 def xxx = {無參數,純code}
說實話,從C/C++語言的角度看,閉包和函數指針很像。閉包定義好后,要調用它的方法就是:
閉包對象.call(參數)
或者更像函數指針調用的方法:
閉包對象(參數)
比如:
aClosure.call("this is string",100) //或者 aClosure("this is string", 100)
實例演練,源碼如下
def fun1 = {
p1 ->
def s = "我是一個閉包," + p1 } println(fun1.call()) //閉包 調用方式1 println(fun1.call("我是一個參數")) //閉包 調用方式2 println(fun1("我是一個參數2"))
運行結果如下:
我是一個閉包,null 我是一個閉包,我是一個參數 我是一個閉包,我是一個參數2
閉包沒定義參數的話,則隱含有一個參數,這個參數名字叫it,和this的作用類似。it代表閉包的參數。
def fun2 = { it-> "dsdsd" } println( fun2.call())
如果在閉包定義時,采用下面這種寫法,則表示閉包沒有參數!
def fun3 = {
-> "dsdsd" } println( fun3.call())
如果調用 fun3 的時候傳遞參數就會報錯,比如
fun3.call("d") //執行這個方法的時候就會報錯
省略圓括號
def list = [1,2,3] //定義一個List //調用它的each,這段代碼的格式看不懂了吧?each是個函數,圓括號去哪了? list.each { println(it) } //結果 /** * 1 * 2 * 3 */
each函數調用的圓括號不見了!原來,Groovy中,當函數的最后一個參數是閉包的話,可以省略圓括號。比如
def fun(int a1,String b1, Closure closure){ //dosomething closure() //調用閉包 }
那么調用的時候,就可以免括號!
fun (4, "test", { println"i am in closure" })
注意,這個特點非常關鍵,因為以后在Gradle中經常會出現這樣的代碼:
task hello{
doLast{ println("hello world") } }
省略圓括號雖然使得代碼簡潔,看起來更像腳本語言
Java 屬性
Groovy中可以像Java那樣寫package,然后寫類。比如我們在 Person.groovy 文件中編寫Java 代碼,如下所示:
然后我們新建 Test.groovy 類,寫測試工程,如下所:
運行結果如下所示:
Person{name='zhaoyanjun', age=20}
- 1
當然,如果不聲明public/private等訪問權限的話,Groovy中類及其變量默認都是public的.
再識 Groovy
Java中,我們最熟悉的是類。但是我們在Java的一個源碼文件中,不能不寫class(interface或者其他….),而Groovy可以像寫腳本一樣,把要做的事情都寫在xxx.groovy中,而且可以通過groovy xxx.groovy直接執行這個腳本。這到底是怎么搞的?
既然是基於Java的,Groovy會先把xxx.groovy中的內容轉換成一個Java類。
在運行完 Test.groovy 后,發現會產生一個 out 目錄,在這個目錄里面可以看到 Person.groovy 、Test.groovy 被轉換成了 .class 文件,如下圖所示:
-
編譯完成后,.groovy 文件都被轉換成了 .class 文件,每個 .class 文件都默認有靜態 main 方法。每一個腳本都會生成一個static main函數。這樣,當我們groovytest.groovy的時候,其實就是用java去執行這個main 函數。
-
腳本中的所有代碼都會放到run函數中。比如,println ‘Groovy world’,這句代碼實際上是包含在run函數里的。
-
Test 繼承 Script 類。
Script 類
在 groovy 的庫文件中,可以看到 Script 類是一個抽象類,繼承 GroovyObjectSupport 類,如下所示
腳本變量的作用域
在 Test.groovy 里面定義變量 s1 , 方法 fun1 , 同時在 fun1 方法中輸出 s1 , 代碼如下:
一運行就報錯,錯誤如下
通過 out 目錄,查看 Test.class 類如下:
可以看到在 s1 變量定義在 run 方法中,相當於局部變量,fun1 方法自然無法訪問 s1 .
解決方法也很簡單 ,就是把 s1 的 def 去掉,代碼如下:
通過 out 目錄,查看 Test.class 類如下:
上圖中 s1 也沒有被定義成 Test 的成員函數,而是在 run 的執行過程中,將 s1 作為一個屬性添加到 Test 實例對象中了。然后在print s1 中,先獲取這個屬性。但是從反編譯的實際上看,s1 並沒有成為 Test.class 的成員變量,其他腳本卻無法訪問 s1 變量 。
怎樣使 s1 徹徹底底變成 Test 的成員變量?
答案也很簡單在 s1 前面加上 @Field 字段
@Field s1 = "123" //s1 徹徹底底變成 Test 的成員變量
- 1
反編譯效果如下:
JSON 操作
JsonOutput 類把對象轉換成 json字符串。
JsonSlurper 類把 json 字符串轉換成對象
定義 Person 實體類
public class Person { String name; int age; }
對象轉json 、 json 轉對象
Person person = new Person(); person.name = "zhaoyanjun" person.age = 27 //把對象轉換為 json 字符串 def json =JsonOutput.toJson(person) println(json) JsonSlurper jsonSlurper = new JsonSlurper() //把字符串轉換為對象 Person person1 = jsonSlurper.parseText(json) println( person1.name )
運行效果如下圖:
{"age":27,"name":"zhaoyanjun"} zhaoyanjun
集合對象轉json 、json 轉集合對象
Person person = new Person(); person.name = "zhaoyanjun" person.age = 27 Person person1 = new Person(); person1.name = "zhaoyanjun2" person1.age = 28 def list = [person,person1] //把集合對象轉換為 json 字符串 def jsonArray =JsonOutput.toJson(list) println(jsonArray) JsonSlurper jsonSlurper = new JsonSlurper() //把字符串轉換為集合對象 List<Person> list2 = jsonSlurper.parseText(jsonArray) println( list2.get(1).name )
運行結果為:
[{"age":27,"name":"zhaoyanjun"},{"age":28,"name":"zhaoyanjun2"}] zhaoyanjun2
I/O 操作
Groovy的 I/O 操作是在原有Java I/O操作上進行了更為簡單方便的封裝,並且使用Closure來簡化代碼編寫。雖然比Java看起來簡單,但要理解起來其實比較難。
文本文件讀
在電腦上新建一個文本文件 test.txt , 內容如下:
今天是星期五
天氣很好
明天就要放假了
下面用 groovy 讀取里面的文本
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath) ; file.eachLine { println it }
你沒有看錯,就是這么簡單,groovy 的文件讀取操作簡單到令人發指。但是這不夠,還有跟令人發指的操作,比如:
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath) ; println file.text //輸出文本
看到這里,發現 Groovy 操作文件比 Java 簡單了 100 倍,蒼天啊!
更多用法
- 指定編碼格式
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath); //指定編碼格式為 utf-8 file.eachLine("utf-8") { println it //讀取文本 }
- 把小寫轉成大寫
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath); file.eachLine { println( it.toUpperCase() ) }
文本文件寫
- 方式1
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath); file.withPrintWriter { it.println("測試") it.println("hello world") }
- 方式2
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath); def out = file.newPrintWriter(); out.println("測試") out.println("hello world") out.flush() out.close()
效果如下:
文件夾操作
- 遍歷文件夾中的文件、文件
def filePath = "C:/Users/T/Desktop/123" def file = new File(filePath); //遍歷 123 文件夾中文件夾 file.eachDir { println "文件夾:"+it.name } //遍歷 123 文件夾中的文件 file.eachFile { println "文件:"+ it.name }
效果如下:
文件夾:1
文件夾:2
文件夾:3
文件:1
文件:2
文件:3
文件:4.txt
深度遍歷文件
def filePath = "e:/" def file = new File(filePath); //深度遍歷目錄,也就是遍歷目錄中的目錄 file.eachDirRecurse { println it.name } //深度遍歷文件,包括目錄和文件 file.eachFileRecurse { println it.path }
InputStream
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath) ; def ism = file.newInputStream() //操作ism,最后記得關掉 ism.close
使用閉包操作 inputStream,以后在Gradle里會常看到這種搞法
def filePath = "C:/Users/T/Desktop/test.txt" def file = new File(filePath) ; file.withInputStream {ism-> // 操作ism. 不用close。Groovy會自動替你close ism.eachLine { println it //讀取文本 } }
確實夠簡單,令人發指。我當年死活也沒找到withInputStream是個啥意思。所以,請各位開發者牢記Groovy I/O操作相關類的SDK地址:
- java.io.File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
- java.io.InputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
- java.io.OutputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
- java.io.Reader: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
- java.io.Writer: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
- java.nio.file.Path: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html
xml 解析
在java中解析xml是非常繁瑣的,通常需要用10行代碼去解析5行的xml文件,非常不經濟。在groovy 中解析xml 就很方便了。
實例1 解析簡單xml
比如下面一段xml
<?xml version="1.0"?> <langs type="current"> <language>Java</language> <language>Groovy</language> <language>JavaScript</language> </langs>
groovy 解析如下:
//獲取 xml 文件的 langs 節點 def langs = new XmlParser().parse("C:/Users/T/Desktop/test.xml") //獲取type 字段的值 def type = langs.attribute("type") println type langs.language.each{ println it.text() }
結果如下:
current
Java
Groovy
JavaScript
實例2 解析復雜 xml
xml 如下圖所示:
<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>com.yiba.sdk</groupId> <artifactId>weshareWiFiSDk</artifactId> <version>2.3.3</version> <versioning> <latest>2.3.3</latest> <versions> <version>2.2.7</version> <version>2.3.0</version> <version>2.3.1</version> <version>2.3.2</version> <version>2.3.3</version> </versions> </versioning> </metadata>
解析代碼如下:
//獲取metadata節點
def metadata = new XmlParser().parse("C:/Users/T/Desktop/test1.xml") //獲取metadata下面的 groupId 屬性值 def groupId = metadata.groupId.text() //獲取metadata下面的 artifactId 屬性值 def artifactId = metadata.artifactId.text() //獲取metadata下面的 version 屬性值 def version = metadata.version.text() println groupId + " " + artifactId + " " + version //獲取metadata下面的 versioning 節點 def versioning = metadata.versioning //獲取versioning 下面的 latest 屬性值 println versioning.latest.text() //獲取versioning 下面的 versions 節點 def versions = versioning.versions versions.version.each{ //遍歷 versions 下面的version 值 println "version" + it.text() }
結果如下:
com.yiba.sdk weshareWiFiSDk 2.3.3 2.3.3 version2.2.7 version2.3.0 version2.3.1 version2.3.2 version2.3.3