Groovy 快速指南



Groovy - 概述

Groovy 是一種基於 Java 平臺的面嚮物件語言。Groovy 1.0 於 2007 年 1 月 2 日釋出,目前主要版本為 Groovy 2.4。Groovy 使用 Apache License v 2.0 許可證發行。

Groovy 的特性

Groovy 具有以下特性:

  • 支援靜態和動態型別。
  • 支援運算子過載。
  • 原生語法支援列表和關聯陣列。
  • 原生支援正則表示式。
  • 原生支援各種標記語言,例如 XML 和 HTML。
  • 對於 Java 開發人員來說,Groovy 很簡單,因為 Java 和 Groovy 的語法非常相似。
  • 您可以使用現有的 Java 庫。
  • Groovy 擴充套件了 java.lang.Object。

Groovy 的官方網站是 http://www.groovy-lang.org/

Groovy Official Website

Groovy - 環境配置

有多種方法可以設定 Groovy 環境。

**二進位制下載和安裝** - 訪問連結 www.groovy-lang.org/download.html 獲取 Windows 安裝程式部分。點選此選項開始下載 Groovy 安裝程式。

Groovy Environment Setup

啟動安裝程式後,按照以下步驟完成安裝。

**步驟 1** - 選擇語言安裝程式。

Language Installer

**步驟 2** - 在下一個螢幕中點選“下一步”按鈕。

Groovy 2.4.5 Setup

**步驟 3** - 點選“我同意”按鈕。

License Agreement

**步驟 4** - 接受預設元件並點選“下一步”按鈕。

Choose Components

**步驟 5** - 選擇合適的目標資料夾,然後點選“下一步”按鈕。

Install Location

**步驟 6** - 點選“安裝”按鈕開始安裝。

Start Menu Folder

**步驟 7** - 安裝完成後,點選“下一步”按鈕開始配置。

Installation Complete

**步驟 8** - 選擇預設選項並點選“下一步”按鈕。

Environment Variables

**步驟 9** - 接受預設檔案關聯並點選“下一步”按鈕。

File Associations

**步驟 10** - 點選“完成”按鈕完成安裝。

Finish Button

完成上述步驟後,您可以啟動 Groovy shell,它是 Groovy 安裝的一部分,有助於測試 Groovy 語言的不同方面,而無需使用完整的 Groovy 整合開發環境。這可以透過在命令提示符下執行命令 groovysh 來完成。

Running Command Groovysh

如果要將 Groovy 二進位制檔案包含在 Maven 或 Gradle 構建中,可以新增以下幾行

Gradle

'org.codehaus.groovy:groovy:2.4.5'

Maven

<groupId>org.codehaus.groovy</groupId> 
<artifactId>groovy</artifactId>  
<version>2.4.5</version>

Groovy - 基本語法

為了理解 Groovy 的基本語法,讓我們首先來看一個簡單的 Hello World 程式。

建立你的第一個 Hello World 程式

建立你的第一個 Hello World 程式就像輸入以下程式碼行一樣簡單:

class Example {
   static void main(String[] args) {
      // Using a simple println statement to print output to the console
      println('Hello World');
   }
}

執行上述程式後,我們將得到以下結果:

Hello World

Groovy 中的匯入語句

匯入語句可用於匯入可在程式碼中使用的其他庫的功能。這是透過使用**import**關鍵字完成的。

以下示例演示如何使用 MarkupBuilder 類的簡單匯入,這可能是用於建立 HTML 或 XML 標記的最常用類之一。

import groovy.xml.MarkupBuilder 
def xml = new MarkupBuilder() 

預設情況下,Groovy 會在您的程式碼中包含以下庫,因此您無需顯式匯入它們。

import java.lang.* 
import java.util.* 
import java.io.* 
import java.net.* 

import groovy.lang.* 
import groovy.util.* 

import java.math.BigInteger 
import java.math.BigDecimal

Groovy 中的標記

標記是關鍵字、識別符號、常量、字串文字或符號。

println(“Hello World”);

在上面的程式碼行中,有兩個標記,第一個是關鍵字 println,第二個是“Hello World”的字串文字。

Groovy 中的註釋

註釋用於記錄程式碼。Groovy 中的註釋可以是單行註釋或多行註釋。

單行註釋透過在行中的任何位置使用 // 來標識。示例如下:

class Example {
   static void main(String[] args) {
      // Using a simple println statement to print output to the console
      println('Hello World');
   }
}

多行註釋以 /* 開頭,以 */ 結尾。

class Example {
   static void main(String[] args) {
      /* This program is the first program
      This program shows how to display hello world */
      println('Hello World');
   }
}

分號

與 Java 程式語言不同,在每個語句結尾處使用分號不是必需的,它是可選的。

class Example {
   static void main(String[] args) {
      def x = 5
      println('Hello World');  
   }
}

如果您執行上述程式,主方法中的兩個語句都不會產生任何錯誤。

識別符號

識別符號用於定義變數、函式或其他使用者定義的變數。識別符號以字母、美元符號或下劃線開頭。它們不能以數字開頭。以下是一些有效識別符號的示例:

def employeename 
def student1 
def student_name

其中**def**是 Groovy 中用於定義識別符號的關鍵字。

這是一個如何在我們的 Hello World 程式中使用識別符號的程式碼示例。

class Example {
   static void main(String[] args) {
      // One can see the use of a semi-colon after each statement
      def x = 5;
      println('Hello World'); 
   }
}

在上面的示例中,變數**x**用作識別符號。

關鍵字

顧名思義,關鍵字是 Groovy 程式語言中保留的特殊單詞。下表列出了 Groovy 中定義的關鍵字。

as assert break case
catch class const continue
def default do else
enum extends false finally
for goto if implements
import in instanceof interface
new pull package return
super switch this throw
throws trait true try
while

空白字元

空白字元是 Java 和 Groovy 等程式語言中用來描述空格、製表符、換行符和註釋的術語。空白字元將語句的一個部分與另一個部分分開,並使編譯器能夠識別語句中的一個元素在哪裡。

例如,在下面的程式碼示例中,關鍵字**def**和變數 x 之間有一個空格。這是為了讓編譯器知道需要使用**def**關鍵字,並且 x 應該是需要定義的變數名。

def x = 5;

字面量

字面量是表示 Groovy 中固定值的表示法。Groovy 語言具有整數、浮點數、字元和字串的表示法。以下是 Groovy 程式語言中字面量的一些示例:

12 
1.45 
‘a’ 
“aa”

Groovy - 資料型別

在任何程式語言中,您都需要使用各種變數來儲存各種型別的資訊。變數只不過是保留的記憶體位置以儲存與變數關聯的值。這意味著當您建立變數時,您會在記憶體中保留一些空間來儲存與該變數關聯的值。

您可能希望儲存各種資料型別的資訊,例如字串、字元、寬字元、整數、浮點數、布林值等。根據變數的資料型別,作業系統分配記憶體並決定可以在保留的記憶體中儲存什麼。

內建資料型別

Groovy 提供了各種內建資料型別。以下是 Groovy 中定義的資料型別列表:

  • **byte** - 用於表示位元組值。例如 2。

  • **short** - 用於表示短整型數。例如 10。

  • **int** - 用於表示整數。例如 1234。

  • **long** - 用於表示長整型數。例如 10000090。

  • **float** - 用於表示 32 位浮點數。例如 12.34。

  • **double** - 用於表示 64 位浮點數,這是有時可能需要的更長的十進位制數表示形式。例如 12.3456565。

  • **char** - 定義單個字元字面量。例如 'a'。

  • **Boolean** - 表示布林值,可以為 true 或 false。

  • **String** - 這些是文字字面量,以**字元鏈**的形式表示。例如“Hello World”。

邊界值

下表顯示數值和十進位制字面量的最大允許值。

byte -128 到 127
short -32,768 到 32,767
int -2,147,483,648 到 2,147,483,647
long -9,223,372,036,854,775,808 到 +9,223,372,036,854,775,807
float 1.40129846432481707e-45 到 3.40282346638528860e+38
double 4.94065645841246544e-324d 到 1.79769313486231570e+308d

數值類

型別 除了原始型別外,還允許使用以下物件型別(有時稱為包裝型別):

  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Float
  • java.lang.Double

此外,以下類可用於支援任意精度算術:

名稱 描述 示例
java.math.BigInteger 不可變的任意精度有符號整數 30g
java.math.BigDecimal 不可變的任意精度有符號十進位制數 3.5g

以下程式碼示例展示瞭如何使用不同的內建資料型別:

class Example { 
   static void main(String[] args) { 
      //Example of a int datatype 
      int x = 5; 
		
      //Example of a long datatype 
      long y = 100L; 
		
      //Example of a floating point datatype 
      float a = 10.56f; 
		
      //Example of a double datatype 
      double b = 10.5e40; 
		
      //Example of a BigInteger datatype 
      BigInteger bi = 30g; 
		
      //Example of a BigDecimal datatype 
      BigDecimal bd = 3.5g; 
		
      println(x); 
      println(y); 
      println(a); 
      println(b); 
      println(bi); 
      println(bd); 
   } 
}

執行上述程式後,我們將得到以下結果:

5 
100 
10.56 
1.05E41 
30 
3.5

Groovy - 變數

Groovy 中的變數可以透過兩種方式定義:使用資料型別的**原生語法**或使用**def 關鍵字**。對於變數定義,必須顯式提供型別名稱或使用“def”代替。這是 Groovy 解析器所必需的。

如上一章所述,Groovy 有以下幾種基本型別的變數:

  • **byte** - 用於表示位元組值。例如 2。

  • **short** - 用於表示短整型數。例如 10。

  • **int** - 用於表示整數。例如 1234。

  • **long** - 用於表示長整型數。例如 10000090。

  • **float** - 用於表示 32 位浮點數。例如 12.34。

  • **double** - 用於表示 64 位浮點數,這是有時可能需要的更長的十進位制數表示形式。例如 12.3456565。

  • **char** - 定義單個字元字面量。例如 'a'。

  • **Boolean** - 表示布林值,可以為 true 或 false。

  • **String** - 這些是文字字面量,以**字元鏈**的形式表示。例如“Hello World”。

Groovy 還允許使用其他型別的變數,例如陣列、結構和類,我們將在後續章節中看到。

變數宣告

變數宣告告訴編譯器在哪裡以及如何建立變數的儲存空間。

以下是一個變數宣告的示例:

class Example { 
   static void main(String[] args) { 
      // x is defined as a variable 
      String x = "Hello";
		
      // The value of the variable is printed to the console 
      println(x);
   }
}

執行上述程式後,我們將得到以下結果:

Hello

變數命名

變數名可以由字母、數字和下劃線組成。它必須以字母或下劃線開頭。大寫字母和小寫字母是不同的,因為 Groovy 與 Java 一樣,是一種區分大小寫的程式語言。

class Example { 
   static void main(String[] args) { 
      // Defining a variable in lowercase  
      int x = 5;
	  
      // Defining a variable in uppercase  
      int X = 6; 
	  
      // Defining a variable with the underscore in it's name 
      def _Name = "Joe"; 
		
      println(x); 
      println(X); 
      println(_Name); 
   } 
}

執行上述程式後,我們將得到以下結果:

5 
6 
Joe 

我們可以看到,由於大小寫敏感性,**x**和**X**是兩個不同的變數,在第三種情況下,我們可以看到 _Name 以下劃線開頭。

列印變數

您可以使用 println 函式列印變數的當前值。以下示例顯示瞭如何實現這一點。

class Example { 
   static void main(String[] args) { 
      //Initializing 2 variables 
      int x = 5; 
      int X = 6; 
	  
      //Printing the value of the variables to the console 
      println("The value of x is " + x + "The value of X is " + X);  
   }
}

執行上述程式後,我們將得到以下結果:

The value of x is 5 The value of X is 6 

Groovy - 運算子

運算子是告訴編譯器執行特定數學或邏輯運算的符號。

Groovy 有以下型別的運算子:

  • 算術運算子
  • 關係運算符
  • 邏輯運算子
  • 位運算子
  • 賦值運算子

算術運算子

Groovy 語言支援與任何其他語言相同的標準算術運算子。以下是 Groovy 中可用的算術運算子:

示例

運算子 描述 示例
+ 兩個運算元的加法 1 + 2 的結果為 3
減法 從第一個運算元中減去第二個運算元 2 − 1 的結果為 1
* 兩個運算元的乘法 2 * 2 的結果為 4
/ 分子除以分母 3 / 2 的結果為 1.5
% 模運算子,以及整數/浮點數除法後的餘數 3 % 2 的結果為 1
++ 增量運算子,用於將運算元的值加 1

int x = 5;

x++;

x 的結果為 6

-- 減量運算子,用於將運算元的值減 1

int x = 5;

x--;

x 的結果為 4

關係運算符

關係運算符允許比較物件。以下是 Groovy 中可用的關係運算符:

示例

運算子 描述 示例
== 測試兩個物件之間的相等性 2 == 2 的結果為 true
!= 測試兩個物件之間的不相等性 3 != 2 的結果為 true
< 檢查左側物件是否小於右側運算元。 2 < 3 的結果為 true
<= 檢查左側物件是否小於或等於右側運算元。 2 <= 3 的結果為 true
> 檢查左側物件是否大於右側運算元。 3 > 2 的結果為 true
>= 檢查左側物件是否大於或等於右側運算元。 3 >= 2 的結果為 true

邏輯運算子

邏輯運算子用於評估布林表示式。以下是 Groovy 中可用的邏輯運算子:

示例

運算子 描述 示例
&& 這是邏輯“與”運算子 true && true 的結果為 true
|| 這是邏輯“或”運算子 true || true 的結果為 true
! 這是邏輯“非”運算子 !false 的結果為 true

位運算子

Groovy 提供四種位運算子。以下是 Groovy 中可用的位運算子:

示例

序號 運算子 & 說明
1

&

這是位“與”運算子

2

|

這是位“或”運算子

3

^

這是位“異或”或“排他或”運算子

4

~

這是位取反運算子

以下是展示這些運算子的真值表。

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

賦值運算子

Groovy 語言還提供賦值運算子。以下是 Groovy 中可用的賦值運算子:

示例

運算子 描述 示例
+= 將右側運算元新增到左側運算元,並將結果賦值給左側運算元。

def A = 5

A+=3

輸出將為 8

-= 從左側運算元中減去右側運算元,並將結果賦值給左側運算元

def A = 5

A-=3

輸出將為 2

*= 將右側運算元乘以左側運算元,並將結果賦值給左側運算元

def A = 5

A*=3

輸出將為 15

/= 將左側運算元除以右側運算元,並將結果賦值給左側運算元

def A = 6

A/=3

輸出將為 2

%= 使用兩個運算元進行模運算,並將結果賦值給左側運算元

def A = 5

A%=3

輸出將為 2

範圍運算子

Groovy 支援範圍的概念,並使用 .. 符號提供範圍運算子的表示法。下面是一個簡單的範圍運算子示例。

def range = 0..5 

這只是定義了一個簡單的整數範圍,儲存在一個名為 range 的區域性變數中,下界為 0,上界為 5。

以下程式碼片段顯示瞭如何使用各種運算子。

class Example { 
   static void main(String[] args) { 
      def range = 5..10; 
      println(range); 
      println(range.get(2)); 
   } 
}

執行上述程式後,我們將得到以下結果:

println語句可以看出,顯示了 range 語句中定義的整個數字範圍。

get 語句用於從定義的範圍中獲取物件,該物件以索引值作為引數。

[5, 6, 7, 8, 9, 10] 
7

運算子優先順序

下表按優先順序順序列出了所有 Groovy 運算子。

序號 運算子 & 名稱
1

++ -- + -

前增量/減量、一元加、一元減

2

* / %

乘法、除法、模運算

3

+ -

加法、減法

4

== != <=>

等於、不等於、比較

5

&

二進位制/按位與

6

^

二進位制/按位異或

7

|

二進位制/按位或

8

&&

邏輯與

9

||

邏輯或

10

= **= *= /= %= += -= <<= >>= >>>= &= ^= |=

各種賦值運算子

Groovy - 迴圈

到目前為止,我們已經看到按順序依次執行的語句。此外,Groovy 中還提供了語句來改變程式邏輯中的控制流。然後,它們被分類為控制流語句,我們將在後面詳細介紹。

序號 語句 & 說明
1 while 語句

while 語句首先評估條件表示式(布林值),如果結果為 true,則執行 while 迴圈中的語句。

2 for 語句

for 語句用於迭代一組值。

3 for-in 語句

for-in 語句用於迭代一組值。

迴圈控制語句

序號 語句 & 說明
1 break 語句

break 語句用於改變迴圈和 switch 語句中的控制流。

2 continue 語句

continue 語句是對 break 語句的補充。它的使用僅限於 while 和 for 迴圈。

Groovy - 條件判斷

決策結構要求程式設計師指定一個或多個條件供程式評估或測試,以及如果條件確定為true,則要執行的語句;或者可選地,如果條件確定為false,則要執行其他語句。

序號 語句 & 說明
1 if 語句

此語句的一般工作方式是,首先評估 if 語句中的條件。如果條件為真,則執行語句。

2 if/else 語句

此語句的一般工作方式是,首先評估 if 語句中的條件。如果條件為真,則執行其後的語句,並在 else 條件之前停止並退出迴圈。如果條件為假,則執行 else 語句塊中的語句,然後退出迴圈。

3 巢狀 if 語句

有時需要在彼此內部巢狀多個 if 語句。

4 switch 語句

有時,巢狀 if-else 語句非常常見且經常使用,因此設計了一個更簡單的語句,稱為 switch 語句。

5 巢狀 switch 語句

也可以巢狀一組 switch 語句。

Groovy - 方法

Groovy 中的方法是用返回型別或def關鍵字定義的。方法可以接收任意數量的引數。定義引數時,不需要顯式定義型別。可以新增 public、private 和 protected 等修飾符。預設情況下,如果沒有提供可見性修飾符,則方法為 public。

最簡單的方法型別是沒有引數的方法,如下所示:

def methodName() { 
   //Method code 
}

以下是一個簡單方法的示例

class Example {
   static def DisplayName() {
      println("This is how methods work in groovy");
      println("This is an example of a simple method");
   } 
	
   static void main(String[] args) {
      DisplayName();
   } 
}

在上面的示例中,DisplayName 是一個簡單的方法,它包含兩個 println 語句,用於向控制檯輸出一些文字。在我們的靜態 main 方法中,我們只是呼叫 DisplayName 方法。上述方法的輸出將為:

This is how methods work in groovy 
This is an example of a simple method

方法引數

如果方法的行為由一個或多個引數的值決定,則該方法通常更有用。我們可以使用方法引數將值傳遞給被呼叫的方法。請注意,引數名稱必須彼此不同。

最簡單的方法型別是有引數的方法,如下所示:

def methodName(parameter1, parameter2, parameter3) { 
   // Method code goes here 
}

以下是一個帶有引數的簡單方法的示例

class Example {
   static void sum(int a,int b) {
      int c = a+b;
      println(c);
   }  
	
   static void main(String[] args) {
      sum(10,5);
   } 
}

在這個例子中,我們建立了一個帶有 2 個引數 a 和 b 的 sum 方法。兩個引數都是 int 型別。然後,我們從 main 方法中呼叫 sum 方法,並將值傳遞給變數 a 和 b。

上述方法的輸出將為 15。

預設引數

Groovy 還允許在方法中為引數指定預設值。如果未向方法傳遞引數的值,則使用預設值。如果同時使用非預設引數和預設引數,則需要注意的是,預設引數必須定義在引數列表的末尾。

以下是一個帶有引數的簡單方法的示例:

def someMethod(parameter1, parameter2 = 0, parameter3 = 0) { 
   // Method code goes here 
} 

讓我們看看之前用於兩個數字相加的示例,並建立一個具有一個預設引數和另一個非預設引數的方法:

class Example { 
   static void sum(int a,int b = 5) { 
      int c = a+b; 
      println(c); 
   } 
	
   static void main(String[] args) {
      sum(6); 
   } 
}

在這個例子中,我們建立了一個帶有兩個引數 a 和 b 的 sum 方法。兩個引數都是 int 型別。本例與前例的區別在於,在本例中,我們為 b 指定了 5 的預設值。因此,當我們從 main 方法呼叫 sum 方法時,可以選擇只傳遞一個值為 6 的值,該值將分配給 sum 方法中的引數 a。

上述方法的輸出將為 11。

class Example {
   static void sum(int a,int b = 5) {
      int c = a+b;
      println(c);
   } 
	
   static void main(String[] args) {
      sum(6,6);
   } 
}

我們也可以透過傳遞 2 個值來呼叫 sum 方法,在我們上面的例子中,我們傳遞了 2 個值為 6 的值。第二個值為 6 的值實際上將替換分配給引數 b 的預設值。

上述方法的輸出將為 12。

方法返回值

方法也可以將值返回給呼叫程式。這在現代程式語言中是必需的,其中方法執行某種計算,然後將所需的值返回給呼叫方法。

以下是一個帶有返回值的簡單方法的示例。

class Example {
   static int sum(int a,int b = 5) {
      int c = a+b;
      return c;
   } 
	
   static void main(String[] args) {
      println(sum(6));
   } 
}

在我們上面的示例中,請注意,這次我們為 sum 方法指定了一個 int 型別的返回值。在方法中,我們使用 return 語句將 sum 值傳送給呼叫主程式。由於該方法的值現在可用於 main 方法,因此我們使用 println 函式在控制檯中顯示該值。

上述方法的輸出將為 11。

例項方法

就像 Java 語言一樣,方法通常在 Groovy 中的類內實現。類只不過是建立不同物件的藍圖或模板,它定義了物件的屬性和行為。類物件表現出其類定義的屬性和行為。因此,行為是透過在類內建立方法來定義的。

我們將在後面的章節中更詳細地介紹類,但以下是在類中實現方法的一個示例。在我們之前的示例中,我們將方法定義為靜態方法,這意味著我們可以直接從類訪問這些方法。下一個方法示例是例項方法,其中透過建立類的物件來訪問方法。我們將在後面的章節中介紹類,現在我們將演示如何使用方法。

以下是如何實現方法的示例。

class Example { 
   int x; 
	
   public int getX() { 
      return x; 
   } 
	
   public void setX(int pX) { 
      x = pX; 
   } 
	
   static void main(String[] args) { 
      Example ex = new Example(); 
      ex.setX(100); 
      println(ex.getX()); 
   } 
}

在我們上面的示例中,請注意,這次我們沒有為類方法指定靜態屬性。在我們的 main 函式中,我們實際上是建立了 Example 類的例項,然後呼叫 ‘ex’ 物件的方法。

上述方法的輸出將為 100。

區域性引數名和外部引數名

Groovy 像 Java 一樣提供了局部引數和全域性引數的功能。在下面的示例中,lx 是一個區域性引數,其作用域僅限於 getX() 函式內,而 x 是一個全域性屬性,可以在整個 Example 類中訪問。如果我們嘗試在 getX() 函式之外訪問變數 lx,我們將得到一個錯誤。

class Example { 
   static int x = 100; 
	
   public static int getX() { 
      int lx = 200; 
      println(lx); 
      return x; 
   } 
	
   static void main(String[] args) { 
      println(getX()); 
   }  
}

執行上述程式後,我們將得到以下結果。

200 
100

Properties 的這種方法

就像在 Java 中一樣,Groovy 可以使用this關鍵字訪問其例項成員。下面的示例展示了當我們使用語句this.x時,它如何引用其例項並相應地設定x的值。

class Example { 
   int x = 100; 
	
   public int getX() { 
      this.x = 200; 
      return x; 
   } 
	
   static void main(String[] args) {
      Example ex = new Example(); 
      println(ex.getX());
   }
}

執行上述程式後,我們將得到在控制檯上列印的結果 200。

Groovy - 檔案 I/O

在處理 I/O 時,Groovy 提供了許多輔助方法。Groovy 提供更簡單的類來為檔案提供以下功能。

  • 讀取檔案
  • 寫入檔案
  • 遍歷檔案樹
  • 讀取和寫入資料物件到檔案

除此之外,您還可以始終使用下面列出的普通 Java 類進行檔案 I/O 操作。

  • java.io.File
  • java.io.InputStream
  • java.io.OutputStream
  • java.io.Reader
  • java.io.Writer

讀取檔案

以下示例將輸出 Groovy 中文字檔案的所有行。eachLine 方法是 Groovy 中 File 類中的內建方法,用於確保讀取文字檔案的每一行。

import java.io.File 
class Example { 
   static void main(String[] args) { 
      new File("E:/Example.txt").eachLine {  
         line -> println "line : $line"; 
      } 
   } 
}

File 類用於例項化一個新物件,該物件將檔名作為引數。然後它採用 eachLine 函式,將其放入名為 line 的變數中並相應地列印它。

如果檔案包含以下行,則將列印它們。

line : Example1
line : Example2

將檔案內容作為整個字串讀取

如果要將檔案的全部內容作為字串獲取,可以使用檔案類的 text 屬性。以下示例演示瞭如何執行此操作。

class Example { 
   static void main(String[] args) { 
      File file = new File("E:/Example.txt") 
      println file.text 
   } 
}

如果檔案包含以下行,則將列印它們。

line : Example1 
line : Example2

寫入檔案

如果要寫入檔案,則需要使用 writer 類將文字輸出到檔案。以下示例演示瞭如何執行此操作。

import java.io.File 
class Example { 
   static void main(String[] args) { 
      new File('E:/','Example.txt').withWriter('utf-8') { 
         writer -> writer.writeLine 'Hello World' 
      }  
   } 
}

如果開啟檔案 Example.txt,您將看到“Hello World”列印到檔案中。

獲取檔案大小

如果要獲取檔案的大小,可以使用檔案類的 length 屬性來獲取檔案的大小。以下示例演示瞭如何執行此操作。

class Example {
   static void main(String[] args) {
      File file = new File("E:/Example.txt")
      println "The file ${file.absolutePath} has ${file.length()} bytes"
   } 
}

以上程式碼將以位元組為單位顯示檔案的大小。

測試檔案是否為目錄

如果要檢視路徑是檔案還是目錄,可以使用 File 類的isFileisDirectory 選項。以下示例演示瞭如何執行此操作。

class Example { 
   static void main(String[] args) { 
      def file = new File('E:/') 
      println "File? ${file.isFile()}" 
      println "Directory? ${file.isDirectory()}" 
   } 
}

以上程式碼將顯示以下輸出:

File? false 
Directory? True

建立目錄

如果要建立新目錄,可以使用 File 類的mkdir 函式。以下示例演示瞭如何執行此操作。

class Example {
   static void main(String[] args) {
      def file = new File('E:/Directory')
      file.mkdir()
   } 
}

如果不存在,則將建立 E:\Directory 目錄。

刪除檔案

如果要刪除檔案,可以使用 File 類的 delete 函式。以下示例演示瞭如何執行此操作。

class Example {
   static void main(String[] args) {
      def file = new File('E:/Example.txt')
      file.delete()
   } 
}

如果檔案存在,則將被刪除。

複製檔案

Groovy 還提供將內容從一個檔案複製到另一個檔案的功能。以下示例演示瞭如何執行此操作。

class Example {
   static void main(String[] args) {
      def src = new File("E:/Example.txt")
      def dst = new File("E:/Example1.txt")
      dst << src.text
   } 
}

將建立檔案 Example1.txt,並將檔案 Example.txt 的所有內容複製到此檔案中。

獲取目錄內容

Groovy 還提供列出驅動器和驅動器中檔案的功能。

以下示例演示瞭如何使用 File 類的listRoots 函式顯示機器上的驅動器。

class Example { 
   static void main(String[] args) { 
      def rootFiles = new File("test").listRoots() 
      rootFiles.each { 
         file -> println file.absolutePath 
      }
   }
}

根據機器上可用的驅動器,輸出可能會有所不同。在標準機器上,輸出將類似於以下內容:

C:\ 
D:\

以下示例演示瞭如何使用 File 類的eachFile 函式列出特定目錄中的檔案。

class Example {
   static void main(String[] args) {
      new File("E:/Temp").eachFile() {  
         file->println file.getAbsolutePath()
      }
   } 
}

輸出將顯示 E:\Temp 目錄中的所有檔案。

如果要遞迴顯示目錄及其子目錄中的所有檔案,則可以使用 File 類的eachFileRecurse 函式。以下示例演示瞭如何執行此操作。

class Example { 
   static void main(String[] args) {
      new File("E:/temp").eachFileRecurse() {
         file -> println file.getAbsolutePath()
      }
   }
} 

輸出將顯示 E:\Temp 目錄及其子目錄(如果存在)中的所有檔案。

Groovy - 可選值

Groovy 是一種“可選”型別語言,在理解該語言的基礎知識時,這種區別非常重要。與 Java(一種“強”型別語言)相比,Java 的編譯器知道每個變數的所有型別,並且可以在編譯時理解和遵守約定。這意味著可以在編譯時確定方法呼叫。

在 Groovy 中編寫程式碼時,開發人員可以靈活地提供或不提供型別。這可以簡化實現,並且如果使用得當,可以以強大且動態的方式為您的應用程式提供服務。

在 Groovy 中,可選型別是透過“def”關鍵字完成的。以下是def 方法用法的示例:

class Example { 
   static void main(String[] args) { 
      // Example of an Integer using def 
      def a = 100; 
      println(a); 
		
      // Example of an float using def 
      def b = 100.10; 
      println(b); 
		
      // Example of an Double using def 
      def c = 100.101; 
      println(c);
		
      // Example of an String using def 
      def d = "HelloWorld"; 
      println(d); 
   } 
} 

從上面的程式中,我們可以看到,我們沒有宣告各個變數為 Integer、float、double 或 string,即使它們包含這些型別的數值。

執行上述程式後,我們將得到以下結果:

100 
100.10 
100.101
HelloWorld

可選型別在開發過程中可能是一個強大的工具,但在開發的後期階段,當代碼變得過於龐大和複雜時,可能會導致可維護性問題。

為了掌握如何在 Groovy 中利用可選型別而不使您的程式碼庫變得難以維護,最好在您的應用程式中採用“鴨子型別”的理念。

如果我們使用鴨子型別重寫上面的程式碼,它將如下所示。變數名稱通常與它們表示的型別相似,這使得程式碼更易於理解。

class Example { 
   static void main(String[] args) { 
      // Example of an Integer using def 
      def aint = 100; 
      println(aint); 
		
      // Example of an float using def 
      def bfloat = 100.10; 
      println(bfloat); 
		
      // Example of an Double using def 
      def cDouble = 100.101; 
      println(cDouble);
		
      // Example of an String using def 
      def dString = "HelloWorld"; 
      println(dString); 
   } 
}

Groovy - 數字

在 Groovy 中,數字實際上表示為物件,它們都是 Integer 類的例項。要使物件執行某些操作,我們需要呼叫在其類中宣告的方法之一。

Groovy 支援整數和浮點數。

  • 整數是不包含小數部分的值。
  • 浮點數是包含小數部分的小數值。

下面顯示了 Groovy 中數字的示例:

Integer x = 5; 
Float y = 1.25; 

其中x 的型別為 Integer,y 為 float。

Groovy 中數字定義為物件的原因通常是因為需要對數字執行運算。在基元型別上提供類的概念稱為包裝類。

預設情況下,Groovy 中提供了以下包裝類。

Wrapper Classes

包裝類的物件包含或包裝其相應的基元資料型別。將基元資料型別轉換為物件的過程稱為裝箱,這是由編譯器處理的。將物件轉換回其相應基元型別稱為拆箱。

示例

以下是一個裝箱和拆箱的示例:

class Example { 
   static void main(String[] args) {
      Integer x = 5,y = 10,z = 0; 
		
      // The the values of 5,10 and 0 are boxed into Integer types 
      // The values of x and y are unboxed and the addition is performed 
      z = x+y; 
      println(z);
   }
}

上述程式的輸出將為 15。在上面的示例中,5、10 和 0 的值首先分別裝箱到 Integer 變數 x、y 和 z 中。然後,當執行 x 和 y 的加法時,值將從它們的 Integer 型別中拆箱。

數字方法

由於 Groovy 中的數字表示為類,以下是可用方法的列表。

序號 方法和描述
1 xxxValue()

此方法將數字作為引數,並根據呼叫的方法返回基元型別。

2 compareTo()

compareTo 方法用於將一個數字與另一個數字進行比較。如果您想比較數字的值,這將非常有用。

3 equals()

該方法確定呼叫該方法的 Number 物件是否等於作為引數傳遞的物件。

4 valueOf()

valueOf 方法返回包含傳遞引數值的相關 Number 物件。

5 toString()

該方法用於獲取表示 Number 物件值的 String 物件。

6 parseInt()

此方法用於獲取特定字串的基元資料型別。parseXxx() 是一個靜態方法,可以有一個或兩個引數。

7 abs()

該方法給出引數的絕對值。引數可以是 int、float、long、double、short、byte。

8 ceil()

ceil 方法給出大於或等於引數的最小整數。

9 floor()

floor 方法給出小於或等於引數的最大整數。

10 rint()

rint 方法返回最接近引數值的整數。

11 round()

round 方法返回最接近的 long 或 int,由方法的返回型別給出。

12 min()

該方法給出兩個引數中較小的一個。引數可以是 int、float、long、double。

13 max()

該方法給出兩個引數中較大的一個。引數可以是 int、float、long、double。

14 exp()

該方法返回自然對數的底數 e 的引數次冪。

15 log()

該方法返回引數的自然對數。

16 pow()

該方法返回第一個引數的第二個引數次冪的值。

17 sqrt()

該方法返回引數的平方根。

18 sin()

該方法返回指定雙精度值的正弦。

19 cos()

該方法返回指定雙精度值的餘弦。

20 tan()

該方法返回指定雙精度值的正切。

21 asin()

該方法返回指定雙精度值的反正弦。

22 acos()

該方法返回指定雙精度值的反餘弦。

23 atan()

該方法返回指定雙精度值的反正切。

24 atan2()

該方法將直角座標 (x, y) 轉換為極座標 (r, theta) 並返回 theta。

25 toDegrees()

該方法將引數值轉換為度數。

26 radian()

該方法將引數值轉換為弧度。

27 random()

該方法用於生成 0.0 到 1.0 之間的隨機數。範圍為:0.0 <= Math.random < 1.0。可以透過使用算術運算來實現不同的範圍。

Groovy - 字串

字串文字在 Groovy 中透過將字串文字括在引號中來構造。

Groovy 提供了多種表示字串文字的方法。Groovy 中的字串可以用單引號 (')、雙引號 ("") 或三引號 ('''') 括起來。此外,用三引號括起來的 Groovy 字串可以跨越多行。

以下是 Groovy 中字串用法的示例:

class Example { 
   static void main(String[] args) { 
      String a = 'Hello Single'; 
      String b = "Hello Double"; 
      String c = "'Hello Triple" + "Multiple lines'";
		
      println(a); 
      println(b); 
      println(c); 
   } 
}

執行上述程式後,我們將得到以下結果:

Hello Single 
Hello Double 
'Hello TripleMultiple lines'

字串索引

Groovy 中的字串是字元的有序序列。字串中的單個字元可以透過其位置來訪問。這是由索引位置給出的。

字串索引從零開始,到字串長度減一結束。Groovy 還允許使用負索引從字串末尾倒數。

以下是 Groovy 中字串索引用法的示例:

class Example { 
   static void main(String[] args) { 
      String sample = "Hello world"; 
      println(sample[4]); // Print the 5 character in the string
		
      //Print the 1st character in the string starting from the back 
      println(sample[-1]); 
      println(sample[1..2]);//Prints a string starting from Index 1 to 2 
      println(sample[4..2]);//Prints a string starting from Index 4 back to 2 
      
   } 
}

執行上述程式後,我們將得到以下結果:

o 
d 
el 
oll 

基本字串操作

首先,讓我們學習 Groovy 中的基本字串操作。它們如下所示。

序號 字串操作和描述
1 兩個字串的連線

字串的連線可以透過簡單的“+”運算子來完成。

2 字串重複

字串的重複可以透過簡單的“*”運算子來完成。

3 字串長度

字串的長度由字串的 length() 方法確定。

字串方法

以下是 String 類支援的方法列表。

序號 方法和描述
1 center()

返回一個新的長度為 numberOfChars 的字串,該字串由接收方在左側和右側用空格字元填充組成。

2 compareToIgnoreCase()

忽略大小寫差異地按字典順序比較兩個字串。

3 concat()

將指定的字串連線到此字串的末尾。

4 eachMatch()

處理給定字串的每個匹配的正則表示式組(參見下一節)子字串。

5 endsWith()

測試此字串是否以指定的字尾結尾。

6 equalsIgnoreCase()

忽略大小寫考慮的情況下,將此字串與另一個字串進行比較。

7 getAt()

它返回索引位置處的字串值。

8 indexOf()

返回此字串中指定子字串第一次出現的索引。

9 matches()

它輸出字串是否與給定的正則表示式匹配。

10 minus()

刪除字串的值部分。

11 next()

此方法由字串類的 ++ 運算子呼叫。它遞增給定字串中的最後一個字元。

12 padLeft()

在字串左側填充空格。

13 padRight()

在字串右側填充空格。

14 plus()

追加一個字串。

15 previous()

此方法由 CharSequence 的 -- 運算子呼叫。

16 replaceAll()

用閉包對該文字的結果替換捕獲組的所有出現。

17 reverse()

建立一個新的字串,它是此字串的反向字串。

18 split()

根據給定正則表示式的匹配項拆分此字串。

19 subString()

返回一個新的字串,它是此字串的子字串。

20 toUpperCase()

將此字串中的所有字元轉換為大寫。

21 toLowerCase()

將此字串中的所有字元轉換為小寫。

Groovy - 範圍

範圍是指定一系列值的簡寫。範圍由序列中的第一個和最後一個值表示,範圍可以是包含的或排除的。包含範圍包括從第一個到最後一個的所有值,而排除範圍包括除最後一個值之外的所有值。以下是一些範圍字面量的示例:

  • 1..10 - 包含範圍的示例
  • 1..<10 - 排除範圍的示例
  • ‘a’..’x’ – 範圍也可以由字元組成
  • 10..1 – 範圍也可以是降序的
  • ‘x’..’a’ – 範圍也可以由字元組成,並且是降序的。

以下是範圍的各種可用方法。

序號 方法和描述
1 contains()

檢查範圍是否包含特定值。

2 get()

返回此範圍中指定位置的元素。

3 getFrom()

獲取此範圍的較小值。

4 getTo()

獲取此範圍的較大值。

5 isReverse()

這是一個反向範圍嗎?向後迭代。

6 size()

返回此範圍中的元素數量。

7 subList()

返回此範圍從指定 fromIndex(包含)到 toIndex(不包含)部分的檢視。

Groovy - 列表

列表是一種用於儲存資料項集合的結構。在 Groovy 中,列表儲存一系列物件引用。列表中的物件引用在序列中佔據一個位置,並由整數索引區分。列表字面量表示為一系列用逗號分隔並用方括號括起來的物件。

要處理列表中的資料,我們必須能夠訪問各個元素。Groovy 列表使用索引運算子 [] 進行索引。列表索引從零開始,指的是第一個元素。

以下是一些列表示例:

  • [11, 12, 13, 14] – 整數值列表
  • [‘Angular’, ‘Groovy’, ‘Java’] – 字串列表
  • [1, 2, [3, 4], 5] – 巢狀列表
  • [‘Groovy’, 21, 2.11] – 物件引用的異構列表
  • [ ] – 空列表

在本節中,我們將討論 Groovy 中可用的列表方法。

序號 方法和描述
1 add()

將新值追加到此列表的末尾。

2 contains()

如果此列表包含指定值,則返回 true。

3 get()

返回此列表中指定位置的元素。

4 isEmpty()

如果此列表不包含任何元素,則返回 true。

5 minus()

建立一個新的列表,其中包含原始列表中的元素,但不包含集合中指定的那些元素。

6 plus()

建立一個新的列表,其中包含原始列表的元素以及集合中指定的那些元素。

7 pop()

從此列表中刪除最後一項。

8 remove()

從此列表中刪除指定位置的元素。

9 reverse()

建立一個新的列表,它是原始列表元素的反轉。

10 size()

獲取此列表中的元素數量。

11 sort()

返回原始列表的排序副本。

Groovy - 對映

對映(也稱為關聯陣列、字典、表和雜湊)是物件引用的無序集合。對映集合中的元素透過鍵值訪問。對映中使用的鍵可以是任何類。當我們插入到對映集合時,需要兩個值:鍵和值。

以下是一些對映示例:

  • [‘TopicName’ : ‘Lists’, ‘Author’ : ‘Raghav’] – 鍵值對集合,其中 TopicName 為鍵,其相應的值。

  • [ : ] – 空對映。

在本節中,我們將討論 Groovy 中可用的對映方法。

序號 方法和描述
1 containsKey()

此對映是否包含此鍵?

2 get()

在此對映中查詢鍵並返回相應的值。如果此對映中沒有此鍵的條目,則返回 null。

3 keySet()

獲取此對映中鍵的集合。

4 put()

在此對映中將指定的值與指定的鍵關聯。如果此對映以前為此鍵包含對映,則舊值將被指定的值替換。

5 size()

返回此對映中鍵值對映的數量。

6 values()

返回此對映中包含的值的集合檢視。

Groovy - 日期和時間

Date 類表示時間中的特定時刻,精度為毫秒。Date 類有兩個建構函式,如下所示。

Date()

語法

public Date()

**引數** – 無。

返回值

分配一個 Date 物件並對其進行初始化,以便它表示分配它的時間,精確到最近的毫秒。

示例

以下是此方法用法示例:

class Example { 
   static void main(String[] args) { 
      Date date = new Date(); 
      
      // display time and date using toString() 
      System.out.println(date.toString()); 
   } 
} 

執行上述程式時,我們將得到以下結果。以下輸出將提供當前日期和時間:

Thu Dec 10 21:31:15 GST 2015

Date (long millisec)

語法

public Date(long millisec)

引數

Millisec – 自標準基準時間(紀元)以來指定的毫秒數。

**返回值** – 分配一個 **Date** 物件並將其初始化為表示自標準基準時間(“紀元”)以來指定的毫秒數,即格林威治標準時間 1970 年 1 月 1 日 00:00:00。

示例

以下是此方法用法示例:

class Example {
   static void main(String[] args) {
      Date date = new Date(100);
      
      // display time and date using toString()
      System.out.println(date.toString());
   } 
}

執行上述程式後,我們將得到以下結果:

Thu Jan 01 04:00:00 GST 1970

以下是 Date 類的給定方法。在接受或返回年份、月份、日期、小時、分鐘和秒值的所有 Date 類方法中,使用以下表示:

  • 年份 y 由整數 y - 1900 表示。

  • 月份由 0 到 11 的整數表示;0 為一月,1 為二月,依此類推;因此 11 為十二月。

  • 日期(月份中的某一天)由 1 到 31 的整數表示,方式與通常一樣。

  • 小時由 0 到 23 的整數表示。因此,從午夜到凌晨 1 點的小時為 0 小時,從中午到下午 1 點的小時為 12 小時。

  • 分鐘由 0 到 59 的整數表示,方式與通常一樣。

  • 秒由 0 到 61 的整數表示。

序號 方法和描述
1 after()

測試此日期是否在指定日期之後。

2 equals()

比較兩個日期是否相等。當且僅當引數不為 null 並且是一個表示與該物件相同的毫秒時間點的 Date 物件時,結果為 true。

3 compareTo()

比較兩個日期的順序。

4 toString()

將此 Date 物件轉換為字串。

5 before()

測試此日期是否在指定日期之前。

6 getTime()

返回此 Date 物件表示的格林威治標準時間 1970 年 1 月 1 日 00:00:00 以來經過的毫秒數。

7 setTime()

將此 Date 物件設定為表示格林威治標準時間 1970 年 1 月 1 日 00:00:00 之後 time 毫秒的時間點。

Groovy - 正則表示式

正則表示式是一種用於查詢文字中子字串的模式。Groovy 使用 ~“regex”表示式原生支援正則表示式。引號中包含的文字表示用於比較的表示式。

例如,我們可以建立一個正則表示式物件,如下所示:

def regex = ~'Groovy'

當 Groovy 運算子 =~ 在 **if** 和 **while** 語句中作為謂詞(返回布林值的表示式)出現時(參見第 8 章),左邊的字串運算元與右邊的正則表示式運算元匹配。因此,以下每個表示式都返回 true 值。

定義正則表示式時,可以使用以下特殊字元:

  • 有兩個特殊的定位字元用於表示一行的開頭和結尾:插入符號 (^) 和美元符號 ($)。

  • 正則表示式還可以包含量詞。加號 (+) 表示一次或多次,應用於表示式的前面元素。星號 (*) 用於表示零次或多次出現。問號 (?) 表示零次或一次。

  • 元字元 { 和 } 用於匹配前面字元的特定數量的例項。

  • 在正則表示式中,句點符號 (.) 可以表示任何字元。這被稱為萬用字元。

  • 正則表示式可以包含字元類。一組字元可以作為一個簡單的字元序列,用元字元 [ 和 ] 括起來,如 [aeiou]。對於字母或數字範圍,可以使用破折號分隔符,如 [a-z] 或 [a-mA-M]。字元類的補集由方括號內的前導插入符號表示,如 [^a-z],表示除指定字元之外的所有字元。以下是一些正則表示式的示例。

'Groovy' =~ 'Groovy' 
'Groovy' =~ 'oo' 
'Groovy' ==~ 'Groovy' 
'Groovy' ==~ 'oo' 
'Groovy' =~ '∧G' 
‘Groovy' =~ 'G$' 
‘Groovy' =~ 'Gro*vy' 'Groovy' =~ 'Gro{2}vy'

Groovy - 異常處理

任何程式語言都需要異常處理來處理執行時錯誤,以便維護應用程式的正常流程。

異常通常會中斷應用程式的正常流程,這就是為什麼我們需要在應用程式中使用異常處理的原因。

異常大致分為以下幾類:

  • **已檢查異常** – 除 RuntimeException 和 Error 之外擴充套件 Throwable 類的類稱為已檢查異常,例如 IOException、SQLException 等。已檢查異常在編譯時進行檢查。

一個典型的例子是 FileNotFoundException。假設你的應用程式中有以下程式碼從 E 盤中的檔案讀取。

class Example {
   static void main(String[] args) {
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file);
   } 
}

如果 E 盤中不存在檔案 (file.txt),則會引發以下異常。

捕獲:java.io.FileNotFoundException: E:\file.txt (系統找不到指定的檔案)。

java.io.FileNotFoundException: E:\file.txt (系統找不到指定的檔案)。

  • **未檢查異常** – 擴充套件 RuntimeException 的類稱為未檢查異常,例如 ArithmeticException、NullPointerException、ArrayIndexOutOfBoundsException 等。未檢查異常在編譯時不進行檢查,而是在執行時進行檢查。

一個經典的例子是`ArrayIndexOutOfBoundsException`,它發生在你嘗試訪問陣列索引時,而該索引大於陣列長度。下面是一個這種錯誤的典型示例。

class Example {
   static void main(String[] args) {
      def arr = new int[3];
      arr[5] = 5;
   } 
}

當執行上述程式碼時,將引發以下異常。

捕獲:java.lang.ArrayIndexOutOfBoundsException: 5

java.lang.ArrayIndexOutOfBoundsException: 5

  • 錯誤 - 錯誤是不可恢復的,例如OutOfMemoryError、VirtualMachineError、AssertionError等。

這些是程式永遠無法從中恢復的錯誤,並且會導致程式崩潰。

下圖顯示了Groovy中異常的層次結構組織方式。它完全基於Java中定義的層次結構。

Hierarchy Of Exceptions

捕獲異常

方法使用trycatch關鍵字的組合來捕獲異常。try/catch塊放置在可能生成異常的程式碼周圍。

try { 
   //Protected code 
} catch(ExceptionName e1) {
   //Catch block 
}

所有可能引發異常的程式碼都放置在受保護的程式碼塊中。

在catch塊中,你可以編寫自定義程式碼來處理異常,以便應用程式可以從異常中恢復。

讓我們來看一個與上面類似的程式碼示例,該示例使用大於陣列大小的索引值訪問陣列。但是這次讓我們將程式碼包裝在try/catch塊中。

class Example {
   static void main(String[] args) {
      try {
         def arr = new int[3];
         arr[5] = 5;
      } catch(Exception ex) {
         println("Catching the exception");
      }
		
      println("Let's move on after the exception");
   }
}

執行上述程式後,我們將得到以下結果:

Catching the exception 
Let's move on after the exception

從上面的程式碼中,我們將有問題的程式碼包裝在try塊中。在catch塊中,我們只是捕獲異常並輸出一條訊息,表明發生了異常。

多個Catch塊

可以有多個catch塊來處理多種型別的異常。對於每個catch塊,根據引發的異常型別,你可以編寫相應的程式碼來處理它。

讓我們修改上面的程式碼,以專門捕獲ArrayIndexOutOfBoundsException。以下是程式碼片段。

class Example {
   static void main(String[] args) {
      try {
         def arr = new int[3];
         arr[5] = 5;
      }catch(ArrayIndexOutOfBoundsException ex) {
         println("Catching the Array out of Bounds exception");
      }catch(Exception ex) {
         println("Catching the exception");
      }
		
      println("Let's move on after the exception");
   } 
}

執行上述程式後,我們將得到以下結果:

Catching the Aray out of Bounds exception 
Let's move on after the exception

從上面的程式碼可以看出,ArrayIndexOutOfBoundsException catch塊首先被捕獲,因為它符合異常的條件。

Finally塊

finally塊位於try塊或catch塊之後。finally程式碼塊總是執行,無論是否發生異常。

使用finally塊,你可以執行任何你想要執行的清理型別語句,無論受保護的程式碼中發生什麼情況。此塊的語法如下所示。

try { 
   //Protected code 
} catch(ExceptionType1 e1) { 
   //Catch block 
} catch(ExceptionType2 e2) { 
   //Catch block 
} catch(ExceptionType3 e3) { 
   //Catch block 
} finally {
   //The finally block always executes. 
}

讓我們修改上面的程式碼並新增finally程式碼塊。以下是程式碼片段。

class Example {
   static void main(String[] args) {
      try {
         def arr = new int[3];
         arr[5] = 5;
      } catch(ArrayIndexOutOfBoundsException ex) {
         println("Catching the Array out of Bounds exception");
      }catch(Exception ex) {
         println("Catching the exception");
      } finally {
         println("The final block");
      }
		
      println("Let's move on after the exception");
   } 
} 

執行上述程式後,我們將得到以下結果:

Catching the Array out of Bounds exception 
The final block 
Let's move on after the exception

以下是Groovy中可用的異常方法:

public String getMessage()

返回關於發生的異常的詳細訊息。此訊息在Throwable建構函式中初始化。

public Throwable getCause()

返回異常的原因,以Throwable物件表示。

public String toString()

返回類的名稱與getMessage()結果的連線。

public void printStackTrace()

將toString()的結果以及堆疊跟蹤列印到System.err(錯誤輸出流)。

public StackTraceElement [] getStackTrace()

返回一個包含堆疊跟蹤中每個元素的陣列。索引為0的元素表示呼叫堆疊的頂部,陣列中的最後一個元素表示呼叫堆疊底部的呼叫方法。

public Throwable fillInStackTrace()

用當前堆疊跟蹤填充此Throwable物件的堆疊跟蹤,新增到堆疊跟蹤中的任何先前資訊。

示例

以下是使用上面一些方法的程式碼示例:

class Example {
   static void main(String[] args) {
      try {
         def arr = new int[3];
         arr[5] = 5;
      }catch(ArrayIndexOutOfBoundsException ex) {
         println(ex.toString());
         println(ex.getMessage());
         println(ex.getStackTrace());  
      } catch(Exception ex) {
         println("Catching the exception");
      }finally {
         println("The final block");
      }
		
      println("Let's move on after the exception");
   } 
}

執行上述程式後,我們將得到以下結果:

java.lang.ArrayIndexOutOfBoundsException: 5 
5 
[org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayPutAtMetaMethod$MyPojoMetaMet 
hodSite.call(IntegerArrayPutAtMetaMethod.java:75), 
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) ,
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) ,
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133) ,
Example.main(Sample:8), sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ,
java.lang.reflect.Method.invoke(Method.java:606),
org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93),
groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325),
groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1443),
org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:893),
groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:287),
groovy.lang.GroovyShell.run(GroovyShell.java:524),
groovy.lang.GroovyShell.run(GroovyShell.java:513),
groovy.ui.GroovyMain.processOnce(GroovyMain.java:652),
groovy.ui.GroovyMain.run(GroovyMain.java:384),
groovy.ui.GroovyMain.process(GroovyMain.java:370),
groovy.ui.GroovyMain.processArgs(GroovyMain.java:129),
groovy.ui.GroovyMain.main(GroovyMain.java:109),
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ,
java.lang.reflect.Method.invoke(Method.java:606),
org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:109),
org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:131),
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ,
java.lang.reflect.Method.invoke(Method.java:606),
com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)]
 
The final block 
Let's move on after the exception 

Groovy - 面向物件

在Groovy中,與任何其他面向物件的語言一樣,存在類和物件的概念來表示程式語言的面向物件特性。Groovy類是資料的集合以及對該資料進行操作的方法。類的資料和方法共同用於表示來自問題域的某個現實世界物件。

Groovy中的類聲明瞭由該類定義的物件的狀態(資料)和行為。因此,Groovy類描述了該類的例項欄位和方法。

以下是一個Groovy類的示例。類的名稱是Student,它有兩個欄位——StudentIDStudentName。在主函式中,我們正在建立此類的物件並將值分配給物件的StudentIDStudentName

class Student {
   int StudentID;
   String StudentName;
	
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.StudentName = "Joe"     
   } 
}

getter和setter方法

在任何程式語言中,隱藏例項成員使用private關鍵字,並提供getter和setter方法來設定和獲取例項變數的值始終是一種實踐。以下示例顯示瞭如何做到這一點。

class Student {
   private int StudentID;
   private String StudentName;
	
   void setStudentID(int pID) {
      StudentID = pID;
   }
	
   void setStudentName(String pName) {
      StudentName = pName;
   }
	
   int getStudentID() {
      return this.StudentID;
   }
	
   String getStudentName() {
      return this.StudentName;
   }
	
   static void main(String[] args) {
      Student st = new Student();
      st.setStudentID(1);
      st.setStudentName("Joe");
		
      println(st.getStudentID());
      println(st.getStudentName());
   } 
}

執行上述程式後,我們將得到以下結果:

1 
Joe 

注意關於上述程式的以下要點:

  • 在類中,studentID和studentName都被標記為private,這意味著它們不能從類外部訪問。

  • 每個例項成員都有其自己的getter和setter方法。getter方法返回例項變數的值,例如方法int getStudentID(),而setter方法設定例項ID的值,例如方法void setStudentName(String pName)

例項方法

通常情況下,在類中包含更多方法是很自然的,這些方法實際上對類執行某種功能。在我們的學生示例中,讓我們新增Marks1、Marks2和Marks3的例項成員來表示學生在3個科目中的分數。然後,我們將新增一個新的例項方法來計算學生的總分。程式碼如下所示。

在下面的示例中,方法Total是一個附加的例項方法,其中包含一些內建的邏輯。

class Student {
   int StudentID;
   String StudentName;
	
   int Marks1;
   int Marks2;
   int Marks3;
	
   int Total() {
      return Marks1+Marks2+Marks3;
   }
	
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.StudentName="Joe";
		
      st.Marks1 = 10;
      st.Marks2 = 20;
      st.Marks3 = 30;
		
      println(st.Total());
   }
}

執行上述程式後,我們將得到以下結果:

60

建立多個物件

還可以建立類的多個物件。以下是如何實現此目的的示例。在這裡,我們建立了3個物件(st、st1和st2),並相應地呼叫它們的例項成員和例項方法。

class Student {
   int StudentID;
   String StudentName;
	
   int Marks1;
   int Marks2;
   int Marks3;
	
   int Total() { 
      return Marks1+Marks2+Marks3;
   } 
	
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.StudentName = "Joe";
		
      st.Marks1 = 10;
      st.Marks2 = 20;
      st.Marks3 = 30;
		
      println(st.Total()); 
   
      Student st1 = new Student();
      st.StudentID = 1;
      st.StudentName = "Joe";
		
      st.Marks1 = 10;
      st.Marks2 = 20;
      st.Marks3 = 40;
		
      println(st.Total());  
        
      Student st3 = new Student();
      st.StudentID = 1;
      st.StudentName = "Joe";
		
      st.Marks1 = 10; 
      st.Marks2 = 20;
      st.Marks3 = 50;
		
      println(st.Total());
   } 
} 

執行上述程式後,我們將得到以下結果:

60 
70 
80 

繼承

繼承可以定義為一個類獲取另一個類的屬性(方法和欄位)的過程。透過使用繼承,資訊以分層方式進行管理。

繼承其他屬性的類稱為子類(派生類、子類),而其屬性被繼承的類稱為超類(基類、父類)。

Extends

extends是用於繼承類屬性的關鍵字。以下是extends關鍵字的語法。在下面的示例中,我們正在執行以下操作:

  • 建立一個名為Person的類。此類有一個名為name的例項成員。

  • 建立一個名為Student的類,它擴充套件自Person類。請注意,在Person類中定義的name例項成員在Student類中被繼承。

  • 在Student類的建構函式中,我們正在呼叫基類建構函式。

  • 在我們的Student類中,我們添加了StudentID和Marks1的2個附加例項成員。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
		
      st.Marks1 = 10;
      st.name = "Joe";
		
      println(st.name);
   }
} 

class Person {
   public String name;
   public Person() {}  
} 

class Student extends Person {
   int StudentID
   int Marks1;
	
   public Student() {
      super();
   } 
}   

執行上述程式後,我們將得到以下結果:

Joe

內部類

內部類是在另一個類中定義的。封閉類可以使用內部類,就像平時一樣。另一方面,內部類可以訪問其封閉類的成員,即使它們是私有的。封閉類以外的類不允許訪問內部類。

以下是一個外部類和內部類的示例。在下面的示例中,我們正在執行以下操作:

  • 建立一個名為Outer的類,這將是我們的外部類。
  • 在我們的Outer類中定義一個名為name的字串。
  • 在我們的Outer類中建立一個內部或巢狀類。
  • 請注意,在內部類中,我們可以訪問在Outer類中定義的name例項成員。
class Example { 
   static void main(String[] args) { 
      Outer outobj = new Outer(); 
      outobj.name = "Joe"; 
      outobj.callInnerMethod() 
   } 
} 

class Outer { 
   String name;
	
   def callInnerMethod() { 
      new Inner().methodA() 
   } 
	
   class Inner {
      def methodA() { 
         println(name); 
      } 
   } 
	
}   

執行上述程式後,我們將得到以下結果:

Joe

抽象類

抽象類表示通用概念,因此不能例項化,而是建立為子類。它們的成員包括欄位/屬性和抽象或具體方法。抽象方法沒有實現,必須由具體子類實現。抽象類必須用abstract關鍵字宣告。抽象方法也必須用abstract關鍵字宣告。

在下面的示例中,請注意Person類現在已成為抽象類,不能例項化。另請注意,抽象類中有一個名為DisplayMarks的抽象方法,它沒有實現細節。在student類中,必須新增實現細節。

class Example { 
   static void main(String[] args) { 
      Student st = new Student(); 
      st.StudentID = 1;
		
      st.Marks1 = 10; 
      st.name="Joe"; 
		
      println(st.name); 
      println(st.DisplayMarks()); 
   } 
} 

abstract class Person { 
   public String name; 
   public Person() { } 
   abstract void DisplayMarks();
}
 
class Student extends Person { 
   int StudentID 
   int Marks1; 
	
   public Student() { 
      super(); 
   } 
	
   void DisplayMarks() { 
      println(Marks1); 
   }  
} 

執行上述程式後,我們將得到以下結果:

Joe 
10 
null

介面

介面定義了一個類需要符合的契約。介面只定義需要實現的方法列表,但不定義方法的實現。介面需要使用interface關鍵字宣告。介面只定義方法簽名。介面的方法始終為public。在介面中使用受保護或私有方法是錯誤的。

以下是一個Groovy中介面的示例。在下面的示例中,我們正在執行以下操作:

  • 建立一個名為Marks的介面並建立一個名為DisplayMarks的介面方法。

  • 在類定義中,我們使用implements關鍵字來實現介面。

  • 因為我們正在實現介面,所以我們必須為DisplayMarks方法提供實現。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.Marks1 = 10;
      println(st.DisplayMarks());
   } 
} 

interface Marks { 
   void DisplayMarks(); 
} 

class Student implements Marks {
   int StudentID
   int Marks1;
	
   void DisplayMarks() {
      println(Marks1);
   }
}

執行上述程式後,我們將得到以下結果:

10
null

Groovy - 泛型

泛型使型別(類和介面)在定義類、介面和方法時成為引數。與在方法宣告中使用的更熟悉的形式引數非常相似,型別引數提供了一種方法,可以使用不同的輸入重複使用相同的程式碼。不同之處在於形式引數的輸入是值,而型別引數的輸入是型別。

集合的泛型

集合類(例如List類)可以被泛化,以便應用程式只接受該型別的集合。下面顯示了泛化的ArrayList的一個示例。以下語句的作用是它只接受字串型別的列表項:

List<String> list = new ArrayList<String>();

在下面的程式碼示例中,我們正在執行以下操作:

  • 建立一個泛化的ArrayList集合,它只儲存字串。
  • 向列表中新增3個字串。
  • 對於列表中的每個專案,列印字串的值。
class Example {
   static void main(String[] args) {
      // Creating a generic List collection
      List<String> list = new ArrayList<String>();
      list.add("First String");
      list.add("Second String");
      list.add("Third String");
		
      for(String str : list) {
         println(str);
      }
   } 
}

上述程式的輸出結果為:

First String 
Second String 
Third String

泛型類

整個類也可以泛化。這使得類在接受任何型別和相應地處理這些型別方面更加靈活。讓我們來看一個如何實現此目標的例子。

在下面的程式中,我們執行以下步驟:

  • 我們建立一個名為 ListType 的類。請注意類定義前面放置的 <T> 關鍵字。這告訴編譯器此類可以接受任何型別。因此,當我們宣告此類的物件時,可以在宣告期間指定一個型別,該型別將替換佔位符 <T>

  • 泛型類具有簡單的 getter 和 setter 方法來處理類中定義的成員變數。

  • 在主程式中,請注意,我們能夠宣告 ListType 類的物件,但型別不同。第一個是 Integer 型別,第二個是 String 型別。

class Example {
   static void main(String[] args) {
      // Creating a generic List collection 
      ListType<String> lststr = new ListType<>();
      lststr.set("First String");
      println(lststr.get()); 
		
      ListType<Integer> lstint = new ListType<>();
      lstint.set(1);
      println(lstint.get());
   }
} 

public class ListType<T> {
   private T localt;
	
   public T get() {
      return this.localt;
   }
	
   public void set(T plocal) {
      this.localt = plocal;
   } 
}

上述程式的輸出結果為:

First String 
1

Groovy - 特性 (Traits)

Traits 是語言的結構性構造,它允許:

  • 行為組合。
  • 介面的執行時實現。
  • 與靜態型別檢查/編譯的相容性

它們可以看作是同時包含預設實現和狀態的介面。Trait 使用 trait 關鍵字定義。

下面是一個 Trait 的示例:

trait Marks {
   void DisplayMarks() {
      println("Display Marks");
   } 
}

然後可以使用 implement 關鍵字以與介面類似的方式實現 Trait。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.Marks1 = 10; 
      println(st.DisplayMarks());
   } 
} 

trait Marks { 
   void DisplayMarks() {
      println("Display Marks");
   } 
} 

class Student implements Marks { 
   int StudentID
   int Marks1;
}

實現介面

Traits 可以實現介面,在這種情況下,介面使用 implements 關鍵字宣告。

下面是一個 Trait 實現介面的示例。在下面的示例中,可以注意到以下關鍵點。

  • 定義了一個帶有 DisplayTotal 方法的 Total 介面。

  • Marks Trait 實現 Total 介面,因此需要為 DisplayTotal 方法提供實現。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      st.Marks1 = 10;
		
      println(st.DisplayMarks());
      println(st.DisplayTotal());
   } 
} 

interface Total {
   void DisplayTotal() 
} 

trait Marks implements Total {
   void DisplayMarks() {
      println("Display Marks");
   }
	
   void DisplayTotal() {
      println("Display Total"); 
   } 
} 

class Student implements Marks { 
   int StudentID
   int Marks1;  
} 

上述程式的輸出結果為:

Display Marks 
Display Total

屬性

Trait 可以定義屬性。下面是一個帶有屬性的 Trait 的示例。

在下面的示例中,整數型別的 Marks1 是一個屬性。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
		
      println(st.DisplayMarks());
      println(st.DisplayTotal());
   } 
	
   interface Total {
      void DisplayTotal() 
   } 
	
   trait Marks implements Total {
      int Marks1;
		
      void DisplayMarks() {
         this.Marks1 = 10;
         println(this.Marks1);
      }
		
      void DisplayTotal() {
         println("Display Total");
      } 
   } 
	
   class Student implements Marks {
      int StudentID 
   }
} 

上述程式的輸出結果為:

10 
Display Total

行為組合

Traits 可用於以受控方式實現多重繼承,避免菱形問題。在下面的程式碼示例中,我們定義了兩個 Traits——**Marks** 和 **Total**。我們的 Student 類實現了這兩個 Traits。由於 student 類擴充套件了這兩個 Traits,因此它能夠訪問這兩個方法——**DisplayMarks** 和 **DisplayTotal**。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
		
      println(st.DisplayMarks());
      println(st.DisplayTotal()); 
   } 
} 

trait Marks {
   void DisplayMarks() {
      println("Marks1");
   } 
} 

trait Total {
   void DisplayTotal() { 
      println("Total");
   } 
}  

class Student implements Marks,Total {
   int StudentID 
}   

上述程式的輸出結果為:

Total 
Marks1

擴充套件 Traits

Traits 可以擴充套件另一個 Trait,在這種情況下,必須使用 **extends** 關鍵字。在下面的程式碼示例中,我們使用 Marks Trait 擴充套件 Total Trait。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      println(st.DisplayMarks());
   } 
} 

trait Marks {
   void DisplayMarks() {
      println("Marks1");
   } 
} 

trait Total extends Marks {
   void DisplayMarks() {
      println("Total");
   } 
}  

class Student implements Total {
   int StudentID 
}

上述程式的輸出結果為:

Total

Groovy - 閉包

閉包是一小段匿名的程式碼塊。它通常只包含幾行程式碼。方法甚至可以將程式碼塊作為引數。

下面是一個簡單的閉包及其外觀示例。

class Example {
   static void main(String[] args) {
      def clos = {println "Hello World"};
      clos.call();
   } 
}

在上面的示例中,程式碼行 - {println "Hello World"} 被稱為閉包。可以透過 call 語句執行此識別符號引用的程式碼塊。

執行上述程式後,我們將得到以下結果:

Hello World

閉包中的形式引數

閉包也可以包含形式引數,使其與 Groovy 中的方法一樣更有用。

class Example {
   static void main(String[] args) {
      def clos = {param->println "Hello ${param}"};
      clos.call("World");
   } 
}

在上面的程式碼示例中,請注意 ${param} 的使用,這導致閉包接受一個引數。當透過 clos.call 語句呼叫閉包時,我們現在可以選擇向閉包傳遞引數。

執行上述程式後,我們將得到以下結果:

Hello World

下一個示例重複了之前的示例併產生相同的結果,但表明可以使用稱為 it 的隱式單引數。這裡的 ‘it’ 是 Groovy 中的關鍵字。

class Example {
   static void main(String[] args) {
      def clos = {println "Hello ${it}"};
      clos.call("World");
   } 
}

執行上述程式後,我們將得到以下結果:

Hello World

閉包和變數

更正式地說,閉包可以引用定義閉包時的變數。下面是如何實現此目標的一個示例。

class Example {     
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = {param -> println "${str1} ${param}"}
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
   } 
}

在上面的示例中,除了向閉包傳遞引數外,我們還定義了一個名為 str1 的變數。閉包也包含該變數以及引數。

執行上述程式後,我們將得到以下結果:

Hello World 
Welcome World

在方法中使用閉包

閉包也可以用作方法的引數。在 Groovy 中,List 和集合等資料型別的許多內建方法都將閉包作為引數型別。

以下示例顯示如何將閉包作為引數傳送到方法。

class Example { 
   def static Display(clo) {
      // This time the $param parameter gets replaced by the string "Inner"         
      clo.call("Inner");
   } 
	
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = { param -> println "${str1} ${param}" }
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
		
      // Passing our closure to a method
      Example.Display(clos);
   } 
}

在上面的示例中:

  • 我們定義了一個名為 Display 的靜態方法,它將閉包作為引數。

  • 然後我們在主方法中定義一個閉包,並將其作為引數傳遞給我們的 Display 方法。

執行上述程式後,我們將得到以下結果:

Hello World 
Welcome World 
Welcome Inner

集合和字串中的閉包

幾個 List、Map 和 String 方法接受閉包作為引數。讓我們來看一下如何在這些資料型別中使用閉包的示例。

使用 List 的閉包

以下示例顯示如何將閉包與 List 一起使用。在下面的示例中,我們首先定義一個簡單的值列表。然後,list 集合型別定義一個名為 .**each** 的函式。此函式將閉包作為引數,並將閉包應用於列表的每個元素。

class Example {
   static void main(String[] args) {
      def lst = [11, 12, 13, 14];
      lst.each {println it}
   } 
}

執行上述程式後,我們將得到以下結果:

11 
12 
13 
14

使用 Map 的閉包

以下示例顯示如何將閉包與 Map 一起使用。在下面的示例中,我們首先定義一個簡單的鍵值對 Map。然後,map 集合型別定義一個名為 .each 的函式。此函式將閉包作為引數,並將閉包應用於 map 的每個鍵值對。

class Example {
   static void main(String[] args) {
      def mp = ["TopicName" : "Maps", "TopicDescription" : "Methods in Maps"]             
      mp.each {println it}
      mp.each {println "${it.key} maps to: ${it.value}"}
   } 
}

執行上述程式後,我們將得到以下結果:

TopicName = Maps 
TopicDescription = Methods in Maps 
TopicName maps to: Maps 
TopicDescription maps to: Methods in Maps

通常,我們可能希望遍歷集合的成員,並且僅當元素滿足某些條件時才應用某些邏輯。這可以透過閉包中的條件語句輕鬆處理。

class Example {
   static void main(String[] args) {
      def lst = [1,2,3,4];
      lst.each {println it}
      println("The list will only display those numbers which are divisible by 2")
      lst.each{num -> if(num % 2 == 0) println num}
   } 
}

上面的示例顯示了在閉包中使用的條件 if(num % 2 == 0) 表示式,用於檢查列表中的每個專案是否能被 2 整除。

執行上述程式後,我們將得到以下結果:

1 
2 
3 
4 
The list will only display those numbers which are divisible by 2.
2 
4 

與閉包一起使用的方法

閉包本身提供了一些方法。

序號 方法和描述
1 find()

find 方法查詢集合中第一個匹配某些條件的值。

2 findAll()

它查詢接收物件中所有匹配閉包條件的值。

3 any() & every()

any 方法迭代集合的每個元素,檢查布林謂詞對於至少一個元素是否有效。

4 collect()

collect 方法迭代集合,使用閉包作為轉換器將每個元素轉換為新值。

Groovy - 註解

註釋 是一種元資料形式,其中它們提供有關程式的資料,而這些資料本身並非程式的一部分。註釋不會直接影響它們註釋的程式碼的操作。

註釋主要用於以下原因:

  • 編譯器資訊——註釋可由編譯器用於檢測錯誤或抑制警告。

  • 編譯時和部署時處理——軟體工具可以處理註釋資訊以生成程式碼、XML 檔案等等。

  • 執行時處理——某些註釋可在執行時進行檢查。

在 Groovy 中,基本註釋如下所示:

@interface - at 符號字元 (@) 指示編譯器接下來的是註釋。

註釋可以定義無主體方法和可選預設值的形式存在的成員。

註釋可以應用於以下型別:

字串型別

下面是一個字串註釋的示例:

@interface Simple { 
   String str1() default "HelloWorld"; 
}

列舉型別

enum DayOfWeek { mon, tue, wed, thu, fri, sat, sun } 
@interface Scheduled {
   DayOfWeek dayOfWeek() 
} 

類型別

@interface Simple {} 
@Simple 
class User {
   String username
   int age
}
 
def user = new User(username: "Joe",age:1); 
println(user.age); 
println(user.username);

註釋成員值

使用註釋時,需要至少設定所有沒有預設值的成員。下面是一個示例。定義 Example 註釋後使用時,需要為其賦值。

@interface Example {
   int status() 
}

@Example(status = 1)

閉包註釋引數

Groovy 中註釋的一個好特性是您也可以使用閉包作為註釋值。因此,註釋可以與各種表示式一起使用。

下面給出了關於此的示例。Onlyif 註釋是基於類值建立的。然後將註釋應用於兩個方法,這兩個方法根據 number 變數的值向 result 變數釋出不同的訊息。

@interface OnlyIf {
   Class value() 
}  

@OnlyIf({ number<=6 }) 
void Version6() {
   result << 'Number greater than 6' 
} 

@OnlyIf({ number>=6 }) 
void Version7() {
   result << 'Number greater than 6' 
}

元註釋

這是 Groovy 中註釋的一個非常有用的特性。有時你可能對一個方法有多個註釋,如下所示。有時多個註釋會變得很混亂。

@Procedure 
@Master class 
MyMasterProcedure {} 

在這種情況下,您可以定義一個元註釋,將多個註釋組合在一起,然後將元註釋應用於該方法。因此,對於上面的示例,您可以首先使用 AnnotationCollector 定義註釋集合。

import groovy.transform.AnnotationCollector
  
@Procedure 
@Master 
@AnnotationCollector

完成後,您可以將以下元註釋應用於該方法:

import groovy.transform.AnnotationCollector
  
@Procedure 
@Master 
@AnnotationCollector
  
@MasterProcedure 
class MyMasterProcedure {}

Groovy - XML

XML 是一種可移植的開源語言,允許程式設計師開發其他應用程式可以讀取的應用程式,無論作業系統和/或開發語言如何。這是用於在應用程式之間交換資料的最常見語言之一。

什麼是 XML?

可擴充套件標記語言 XML 是一種標記語言,類似於 HTML 或 SGML。這是全球資訊網聯盟推薦的,並作為開放標準提供。XML 對於跟蹤少量到中等數量的資料非常有用,而無需基於 SQL 的主幹。

Groovy 中的 XML 支援

Groovy 語言還提供了豐富的 XML 語言支援。使用的兩個最基本的 XML 類是:

  • XML 標記構建器——Groovy 支援基於樹的標記生成器 BuilderSupport,可以對其進行子類化以建立各種樹狀物件表示。通常,這些構建器用於表示 XML 標記、HTML 標記。Groovy 的標記生成器捕獲對偽方法的呼叫,並將它們轉換為樹結構的元素或節點。這些偽方法的引數被視為節點的屬性。作為方法呼叫一部分的閉包被視為結果樹節點的巢狀子內容。

  • XML 解析器 − Groovy 的 XmlParser 類採用簡單的模型將 XML 文件解析成 Node 例項的樹。每個 Node 都有 XML 元素的名稱、元素的屬性以及對任何子 Node 的引用。此模型足以滿足大多數簡單的 XML 處理需求。

對於我們所有的 XML 程式碼示例,讓我們使用以下簡單的 XML 檔案 movies.xml 來構建 XML 檔案並隨後讀取檔案。

<collection shelf = "New Arrivals"> 

   <movie title = "Enemy Behind"> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2003</year> 
      <rating>PG</rating> 
      <stars>10</stars> 
      <description>Talk about a US-Japan war</description> 
   </movie> 
	
   <movie title = "Transformers"> 
      <type>Anime, Science Fiction</type>
      <format>DVD</format> 
      <year>1989</year> 
      <rating>R</rating> 
      <stars>8</stars> 
      <description>A schientific fiction</description> 
   </movie> 
	
   <movie title = "Trigun"> 
      <type>Anime, Action</type> 
      <format>DVD</format> 
      <year>1986</year> 
      <rating>PG</rating> 
      <stars>10</stars> 
      <description>Vash the Stam pede!</description> 
   </movie> 
	
   <movie title = "Ishtar"> 
      <type>Comedy</type> 
      <format>VHS</format> 
      <year>1987</year> 
      <rating>PG</rating> 
      <stars>2</stars> 
      <description>Viewable boredom </description> 
   </movie> 
	
</collection> 

XML 標記構建器

語法

public MarkupBuilder()

MarkupBuilder 用於構建整個 XML 文件。XML 文件的建立首先是建立 XML 文件類的物件。建立物件後,可以呼叫偽方法來建立 XML 文件的各種元素。

讓我們來看一個如何建立一個塊(即上面 XML 文件中的一個 movie 元素)的示例:

import groovy.xml.MarkupBuilder 

class Example {
   static void main(String[] args) {
      def mB = new MarkupBuilder()
		
      // Compose the builder
      mB.collection(shelf : 'New Arrivals') {
         movie(title : 'Enemy Behind')
         type('War, Thriller')
         format('DVD')
         year('2003')
         rating('PG')
         stars(10)
         description('Talk about a US-Japan war') 
      }
   } 
}

在上面的示例中,需要注意以下幾點:

  • mB.collection() − 這是一個標記生成器,用於建立 `` 的頭部 XML 標籤。

  • movie(title : 'Enemy Behind') − 這些偽方法使用此方法建立具有值的子標籤。透過指定名為 title 的值,這實際上表示需要為元素建立一個屬性。

  • 向偽方法提供閉包以建立 XML 文件的其餘元素。

  • 類 MarkupBuilder 的預設建構函式已初始化,以便生成的 XML 傳送到標準輸出流。

執行上述程式後,我們將得到以下結果:

<collection shelf = 'New Arrivals'> 
   <movie title = 'Enemy Behind' /> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2003</year> 
      <rating>PG</rating> 
      <stars>10</stars> 
      <description>Talk about a US-Japan war</description> 
   </movie> 
</collection>

為了建立整個 XML 文件,需要執行以下操作。

  • 需要建立一個對映條目來儲存元素的不同值。
  • 對於對映的每個元素,我們將值賦給每個元素。
import groovy.xml.MarkupBuilder 

class Example {
   static void main(String[] args) {
      def mp = [1 : ['Enemy Behind', 'War, Thriller','DVD','2003', 
         'PG', '10','Talk about a US-Japan war'],
         2 : ['Transformers','Anime, Science Fiction','DVD','1989', 
         'R', '8','A scientific fiction'],
         3 : ['Trigun','Anime, Action','DVD','1986', 
         'PG', '10','Vash the Stam pede'],
         4 : ['Ishtar','Comedy','VHS','1987', 'PG', 
         '2','Viewable boredom ']] 
			
      def mB = new MarkupBuilder()  
		
      // Compose the builder
      def MOVIEDB = mB.collection('shelf': 'New Arrivals') {
         mp.each {
            sd -> 
            mB.movie('title': sd.value[0]) {  
               type(sd.value[1])
               format(sd.value[2])
               year(sd.value[3]) 
               rating(sd.value[4])
               stars(sd.value[4]) 
               description(sd.value[5]) 
            }
         }
      }
   } 
} 

執行上述程式後,我們將得到以下結果:

<collection shelf = 'New Arrivals'> 
   <movie title = 'Enemy Behind'> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2003</year> 
      <rating>PG</rating> 
      <stars>PG</stars> 
      <description>10</description> 
   </movie> 
   <movie title = 'Transformers'> 
      <type>Anime, Science Fiction</type> 
      <format>DVD</format> 
      <year>1989</year>
	  <rating>R</rating> 
      <stars>R</stars> 
      <description>8</description> 
   </movie> 
   <movie title = 'Trigun'> 
      <type>Anime, Action</type> 
      <format>DVD</format> 
      <year>1986</year> 
      <rating>PG</rating> 
      <stars>PG</stars> 
      <description>10</description> 
   </movie> 
   <movie title = 'Ishtar'> 
      <type>Comedy</type> 
      <format>VHS</format> 
      <year>1987</year> 
      <rating>PG</rating> 
      <stars>PG</stars> 
      <description>2</description> 
   </movie> 
</collection> 

XML 解析

Groovy 的 XmlParser 類採用簡單的模型將 XML 文件解析成 Node 例項的樹。每個 Node 都有 XML 元素的名稱、元素的屬性以及對任何子 Node 的引用。此模型足以滿足大多數簡單的 XML 處理需求。

語法

public XmlParser() 
   throws ParserConfigurationException, 
      SAXException

以下程式碼展示瞭如何使用 XML 解析器讀取 XML 文件的示例。

讓我們假設我們有相同的文件 Movies.xml,並且我們想要解析 XML 文件並向用戶顯示正確的輸出。以下程式碼片段展示了我們如何遍歷 XML 文件的整個內容並向用戶顯示正確的響應。

import groovy.xml.MarkupBuilder 
import groovy.util.*

class Example {

   static void main(String[] args) { 
	
      def parser = new XmlParser()
      def doc = parser.parse("D:\\Movies.xml");
		
      doc.movie.each{
         bk->
         print("Movie Name:")
         println "${bk['@title']}"
			
         print("Movie Type:")
         println "${bk.type[0].text()}"
			
         print("Movie Format:")
         println "${bk.format[0].text()}"
			
         print("Movie year:")
         println "${bk.year[0].text()}"
			
         print("Movie rating:")
         println "${bk.rating[0].text()}"
			
         print("Movie stars:")
         println "${bk.stars[0].text()}"
			
         print("Movie description:")
         println "${bk.description[0].text()}"
         println("*******************************")
      }
   }
} 

執行上述程式後,我們將得到以下結果:

Movie Name:Enemy Behind 
Movie Type:War, Thriller 
Movie Format:DVD 
Movie year:2003 
Movie rating:PG 
Movie stars:10 
Movie description:Talk about a US-Japan war 
******************************* 
Movie Name:Transformers 
Movie Type:Anime, Science Fiction 
Movie Format:DVD 
Movie year:1989 
Movie rating:R 
Movie stars:8 
Movie description:A schientific fiction 
******************************* 
Movie Name:Trigun 
Movie Type:Anime, Action
Movie Format:DVD 
Movie year:1986 
Movie rating:PG 
Movie stars:10 
Movie description:Vash the Stam pede! 
******************************* 
Movie Name:Ishtar 
Movie Type:Comedy 
Movie Format:VHS 
Movie year:1987 
Movie rating:PG 
Movie stars:2 
Movie description:Viewable boredom

關於上述程式碼,需要注意的重要事項。

  • 正在形成 XmlParser 類的物件,以便可以用來解析 XML 文件。

  • 解析器被賦予 XML 檔案的位置。

  • 對於每個 movie 元素,我們使用閉包來瀏覽每個子節點並顯示相關資訊。

對於 movie 元素本身,我們使用 @ 符號來顯示附加到 movie 元素的 title 屬性。

Groovy - JMX

JMX 是事實上的標準,用於監控所有與 Java 虛擬環境相關的應用程式。鑑於 Groovy 直接位於 Java 之上,Groovy 可以利用 Java 已經完成的大量 JMX 工作。

監控 JVM

可以使用 java.lang.management 中提供的標準類來執行 JVM 的監控。以下程式碼示例展示瞭如何做到這一點。

import java.lang.management.*

def os = ManagementFactory.operatingSystemMXBean 
println """OPERATING SYSTEM: 
\tOS architecture = $os.arch 
\tOS name = $os.name 
\tOS version = $os.version 
\tOS processors = $os.availableProcessors 
""" 
 
def rt = ManagementFactory.runtimeMXBean 
println """RUNTIME: 
   \tRuntime name = $rt.name 
   \tRuntime spec name = $rt.specName 
   \tRuntime vendor = $rt.specVendor 
   \tRuntime spec version = $rt.specVersion 
   \tRuntime management spec version = $rt.managementSpecVersion 
   """ 

def mem = ManagementFactory.memoryMXBean 
def heapUsage = mem.heapMemoryUsage 
def nonHeapUsage = mem.nonHeapMemoryUsage 

println """MEMORY: 
   HEAP STORAGE: 
      \tMemory committed = $heapUsage.committed 
      \tMemory init = $heapUsage.init 
      \tMemory max = $heapUsage.max 
      \tMemory used = $heapUsage.used NON-HEAP STORAGE: 
      \tNon-heap memory committed = $nonHeapUsage.committed 
      \tNon-heap memory init = $nonHeapUsage.init 
      \tNon-heap memory max = $nonHeapUsage.max 
      \tNon-heap memory used = $nonHeapUsage.used 
   """
  
println "GARBAGE COLLECTION:" 
ManagementFactory.garbageCollectorMXBeans.each { gc ->
   println "\tname = $gc.name"
   println "\t\tcollection count = $gc.collectionCount"
   println "\t\tcollection time = $gc.collectionTime"
   String[] mpoolNames =   gc.memoryPoolNames
	
   mpoolNames.each { 
      mpoolName -> println "\t\tmpool name = $mpoolName"
   } 
}

程式碼執行後,輸出將根據執行程式碼的系統而有所不同。下面給出了輸出示例。

OPERATING SYSTEM: 
   OS architecture = x86 
   OS name = Windows 7 
   OS version = 6.1 
   OS processors = 4
   
RUNTIME: 
   Runtime name = 5144@Babuli-PC 
   Runtime spec name = Java Virtual Machine Specification 
   Runtime vendor = Oracle Corporation 
   Runtime spec version = 1.7 
   Runtime management spec version = 1.2
   
MEMORY: 
   HEAP STORAGE: 
      Memory committed = 16252928 
      Memory init = 16777216 
      Memory max = 259522560 
      Memory used = 7355840
   
NON-HEAP STORAGE: 
   Non-heap memory committed = 37715968 
   Non-heap memory init = 35815424 
   Non-heap memory max = 123731968 
   Non-heap memory used = 18532232 
   
GARBAGE COLLECTION: 
   name = Copy 
   collection count = 15 
   collection time = 47 
   mpool name = Eden Space 
   mpool name = Survivor Space
		
   name = MarkSweepCompact 
      collection count = 0 
      collection time = 0 
		
      mpool name = Eden Space 
      mpool name = Survivor Space 
      mpool name = Tenured Gen 
      mpool name = Perm Gen 
      mpool name = Perm Gen [shared-ro] 
      mpool name = Perm Gen [shared-rw]

監控 Tomcat

為了監控 Tomcat,啟動 Tomcat 時應設定以下引數:

set JAVA_OPTS = -Dcom.sun.management.jmxremote 
Dcom.sun.management.jmxremote.port = 9004\
 
-Dcom.sun.management.jmxremote.authenticate=false 
Dcom.sun.management.jmxremote.ssl = false

以下程式碼使用 JMX 來發現正在執行的 Tomcat 中可用的 MBean,確定哪些是 Web 模組並提取每個 Web 模組的處理時間。

import groovy.swing.SwingBuilder
  
import javax.management.ObjectName 
import javax.management.remote.JMXConnectorFactory as JmxFactory 
import javax.management.remote.JMXServiceURL as JmxUrl 
import javax.swing.WindowConstants as WC 
 
import org.jfree.chart.ChartFactory 
import org.jfree.data.category.DefaultCategoryDataset as Dataset 
import org.jfree.chart.plot.PlotOrientation as Orientation 
 
def serverUrl = 'service:jmx:rmi:///jndi/rmi://:9004/jmxrmi' 
def server = JmxFactory.connect(new JmxUrl(serverUrl)).MBeanServerConnection 
def serverInfo = new GroovyMBean(server, 'Catalina:type = Server').serverInfo 
println "Connected to: $serverInfo" 
 
def query = new ObjectName('Catalina:*') 
String[] allNames = server.queryNames(query, null) 

def modules = allNames.findAll { name -> 
   name.contains('j2eeType=WebModule') 
}.collect{ new GroovyMBean(server, it) }
  
println "Found ${modules.size()} web modules. Processing ..." 
def dataset = new Dataset() 
 
modules.each { m ->
   println m.name()
   dataset.addValue m.processingTime, 0, m.path 
}

Groovy - JSON

本章介紹如何使用 Groovy 語言來解析和生成 JSON 物件。

JSON 函式

序號 函式和庫
1

JsonSlurper

JsonSlurper 是一個類,它將 JSON 文字或讀取器內容解析成 Groovy 資料

結構,例如對映、列表和原始型別,例如 Integer、Double、Boolean 和 String。

2

JsonOutput

此方法負責將 Groovy 物件序列化為 JSON 字串。

使用 JsonSlurper 解析資料

JsonSlurper 是一個類,它將 JSON 文字或讀取器內容解析成 Groovy 資料結構,例如對映、列表和原始型別,例如 Integer、Double、Boolean 和 String。

語法

def slurper = new JsonSlurper()

JSON slurper 將文字或讀取器內容解析成列表和對映的資料結構。

JsonSlurper 類附帶了一些解析器實現的變體。有時,在解析某些字串時,您可能會有不同的要求。讓我們以一個需要讀取從 Web 伺服器響應返回的 JSON 的例項為例。在這種情況下,使用解析器 JsonParserLax 變體是有益的。此解析器允許 JSON 文字中的註釋以及無引號字串等。要指定這種解析器,需要在定義 JsonSlurper 物件時使用 JsonParserType.LAX 解析器型別。

讓我們看看下面給出的示例。此示例使用 http 模組從 Web 伺服器獲取 JSON 資料。對於這種型別的遍歷,最佳選擇是將解析器型別設定為 JsonParserLax 變體。

http.request( GET, TEXT ) {
   headers.Accept = 'application/json'
   headers.'User-Agent' = USER_AGENT
	
   response.success = { 
      res, rd ->  
      def jsonText = rd.text 
		
      //Setting the parser type to JsonParserLax
      def parser = new JsonSlurper().setType(JsonParserType.LAX)
      def jsonResp = parser.parseText(jsonText)
   }
}

同樣,Groovy 中還提供了以下其他解析器型別:

  • JsonParserCharArray 解析器基本上採用 JSON 字串並在底層字元陣列上進行操作。在值轉換過程中,它會複製字元子陣列(一種稱為“切片”的機制)並在其上單獨操作。

  • JsonFastParser 是 JsonParserCharArray 的一個特殊變體,也是最快的解析器。JsonFastParser 也稱為索引覆蓋解析器。在給定 JSON 字串的解析過程中,它儘可能避免建立新的字元陣列或字串例項。它只保留指向底層原始字元陣列的指標。此外,它儘可能延遲物件建立。

  • JsonParserUsingCharacterSource 是一個用於超大型檔案的特殊解析器。它使用一種稱為“字元視窗”的技術來解析大型 JSON 檔案(在這種情況下,大型意味著超過 2MB 大小的檔案),並具有恆定的效能特徵。

解析文字

讓我們來看一些如何使用 JsonSlurper 類的示例。

import groovy.json.JsonSlurper 

class Example {
   static void main(String[] args) {
      def jsonSlurper = new JsonSlurper()
      def object = jsonSlurper.parseText('{ "name": "John", "ID" : "1"}') 
		
      println(object.name);
      println(object.ID);
   } 
}

在上面的示例中,我們:

  • 首先建立一個 JsonSlurper 類的例項

  • 然後,我們使用 JsonSlurper 類的 parseText 函式來解析一些 JSON 文字。

  • 當我們獲得物件時,您可以看到我們實際上可以透過鍵訪問 JSON 字串中的值。

上面程式的輸出如下:

John 
1

解析整數列表

讓我們來看另一個 JsonSlurper 解析方法的示例。在下面的示例中,我們正在解析整數列表。您會從以下程式碼中注意到,我們可以使用每個的 List 方法並向其傳遞閉包。

import groovy.json.JsonSlurper 
class Example {
   static void main(String[] args) {
      def jsonSlurper = new JsonSlurper()
      Object lst = jsonSlurper.parseText('{ "List": [2, 3, 4, 5] }')
      lst.each { println it }
   } 
}

上面程式的輸出如下:

List=[2, 3, 4, 5]

解析原始資料型別的列表

JSON 解析器還支援字串、數字、物件、true、false 和 null 的原始資料型別。JsonSlurper 類將這些 JSON 型別轉換為相應的 Groovy 型別。

以下示例展示瞭如何使用 JsonSlurper 解析 JSON 字串。在這裡您可以看到 JsonSlurper 能夠將各個專案解析成它們各自的原始型別。

import groovy.json.JsonSlurper 
class Example {

   static void main(String[] args) {
      def jsonSlurper = new JsonSlurper()
      def obj = jsonSlurper.parseText ''' {"Integer": 12, "fraction": 12.55, "double": 12e13}'''
		
      println(obj.Integer);
      println(obj.fraction);
      println(obj.double); 
   } 
}

上面程式的輸出如下:

12 
12.55 
1.2E+14 

JsonOutput

現在讓我們討論如何在 Json 中列印輸出。這可以透過 JsonOutput 方法來實現。此方法負責將 Groovy 物件序列化為 JSON 字串。

語法

Static string JsonOutput.toJson(datatype obj)

引數 − 引數可以是資料型別的物件——數字、布林值、字元、字串、日期、對映、閉包等。

返回型別 − 返回型別是 json 字串。

示例

下面是一個簡單的示例,說明如何實現這一點。

import groovy.json.JsonOutput 
class Example {
   static void main(String[] args) {
      def output = JsonOutput.toJson([name: 'John', ID: 1])
      println(output);  
   }
}

上面程式的輸出如下:

{"name":"John","ID":1}

JsonOutput 也可用於普通的 Groovy 物件。在下面的示例中,您可以看到我們實際上正在將 Student 型別的物件傳遞給 JsonOutput 方法。

import groovy.json.JsonOutput  
class Example {
   static void main(String[] args) {
      def output = JsonOutput.toJson([ new Student(name: 'John',ID:1),
         new Student(name: 'Mark',ID:2)])
      println(output);  
   } 
}
 
class Student {
   String name
   int ID; 
}

上面程式的輸出如下:

[{"name":"John","ID":1},{"name":"Mark","ID":2}]

Groovy - DSL

Groovy 允許省略頂級語句方法呼叫引數周圍的括號。這被稱為“命令鏈”功能。此擴充套件允許連結此類無括號的方法呼叫,既不需要引數周圍的括號,也不需要連結呼叫之間的點。

如果呼叫以 **a b c d** 執行,這實際上等效於 **a(b).c(d)**。

DSL 或領域特定語言旨在簡化 Groovy 中編寫的程式碼,使其更容易被普通使用者理解。以下示例說明了領域特定語言的確切含義。

def lst = [1,2,3,4] 
print lst

上面的程式碼展示了使用 println 語句將數字列表列印到控制檯。在領域特定語言中,命令將是:

Given the numbers 1,2,3,4
 
Display all the numbers

因此,上面的示例顯示了程式語言的轉換,以滿足領域特定語言的需求。

讓我們來看一個如何在 Groovy 中實現 DSL 的簡單示例:

class EmailDsl {  
   String toText 
   String fromText 
   String body 
	
   /** 
   * This method accepts a closure which is essentially the DSL. Delegate the 
   * closure methods to 
   * the DSL class so the calls can be processed 
   */ 
   
   def static make(closure) { 
      EmailDsl emailDsl = new EmailDsl() 
      // any method called in closure will be delegated to the EmailDsl class 
      closure.delegate = emailDsl
      closure() 
   }
   
   /** 
   * Store the parameter as a variable and use it later to output a memo 
   */ 
	
   def to(String toText) { 
      this.toText = toText 
   }
   
   def from(String fromText) { 
      this.fromText = fromText 
   }
   
   def body(String bodyText) { 
      this.body = bodyText 
   } 
}

EmailDsl.make { 
   to "Nirav Assar" 
   from "Barack Obama" 
   body "How are things? We are doing well. Take care" 
}

執行上述程式後,我們將得到以下結果:

How are things? We are doing well. Take care

關於上述程式碼實現,需要注意以下幾點:

  • 使用接受閉包的靜態方法。這通常是實現 DSL 的一種輕鬆的方法。

  • 在電子郵件示例中,EmailDsl 類有一個 make 方法。它建立一個例項並將閉包中的所有呼叫委託給該例項。這就是“to”和“from”部分最終執行 EmailDsl 類內部方法的機制。

  • 呼叫 to() 方法後,我們將文字儲存在例項中以便稍後格式化。

  • 我們現在可以使用易於理解的簡單語言來呼叫 EmailDSL 方法。

Groovy - 資料庫

Groovy 的 groovy-sql 模組在當前 Java 的 JDBC 技術之上提供了更高級別的抽象。Groovy sql API 支援各種資料庫,其中一些如下所示。

  • HSQLDB
  • Oracle
  • SQL Server
  • MySQL
  • MongoDB

本例中,我們將使用 MySQL 資料庫作為示例。為了在 Groovy 中使用 MySQL,首先需要從 MySQL 官網下載 MySQL JDBC jar 檔案。MySQL 的格式如下所示。

mysql-connector-java-5.1.38-bin

然後,確保將上述 jar 檔案新增到工作站的類路徑中。

資料庫連線

在連線到 MySQL 資料庫之前,請確保以下幾點:

  • 您已建立資料庫 TESTDB。
  • 您已在 TESTDB 中建立表 EMPLOYEE。
  • 該表包含欄位 FIRST_NAME、LAST_NAME、AGE、SEX 和 INCOME。
  • 已設定使用者 ID “testuser” 和密碼 “test123” 用於訪問 TESTDB。
  • 確保您已下載 mysql jar 檔案並將其新增到類路徑中。
  • 您已學習 MySQL 教程以瞭解MySQL 基礎知識

以下示例演示如何連線到 MySQL 資料庫 "TESTDB"。

import java.sql.*; 
import groovy.sql.Sql 

class Example {
   static void main(String[] args) {
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 
         'testuser', 'test123', 'com.mysql.jdbc.Driver')
			
      // Executing the query SELECT VERSION which gets the version of the database
      // Also using the eachROW method to fetch the result from the database
   
      sql.eachRow('SELECT VERSION()'){ row ->
         println row[0]
      }
		
      sql.close()  
   } 
} 

執行此指令碼時,會產生以下結果:

5.7.10-log 
The Sql.newInstance method is used to establish a connection to the database.

建立資料庫表

連線到資料庫後的下一步是在資料庫中建立表。以下示例演示如何使用 Groovy 在資料庫中建立表。Sql 類的 execute 方法用於對資料庫執行語句。

import java.sql.*; 
import groovy.sql.Sql 

class Example { 
   static void main(String[] args) {
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser',  
         'test123', 'com.mysql.jdbc.Driver')
			
      def sqlstr = """CREATE TABLE EMPLOYEE ( 
         FIRST_NAME CHAR(20) NOT NULL,
         LAST_NAME CHAR(20),
         AGE INT,
         SEX CHAR(1),
         INCOME FLOAT )""" 
							
      sql.execute(sqlstr);
      sql.close() 
   } 
}

插入操作

當您想要將記錄插入資料庫表時,需要執行插入操作。

示例

以下示例將在 employee 表中插入一條記錄。程式碼位於 try catch 塊中,以便如果記錄成功執行,則將事務提交到資料庫。如果事務失敗,則執行回滾。

import java.sql.*; 
import groovy.sql.Sql 

class Example {
   static void main(String[] args) { 
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser', 
         'test123', 'com.mysql.jdbc.Driver')
			
      sql.connection.autoCommit = false
		
      def sqlstr = """INSERT INTO EMPLOYEE(FIRST_NAME,
         LAST_NAME, AGE, SEX, INCOME) VALUES ('Mac', 'Mohan', 20, 'M', 2000)""" 
      try {
         sql.execute(sqlstr);
         sql.commit()
         println("Successfully committed") 
      }catch(Exception ex) {
         sql.rollback()
         println("Transaction rollback") 
      }
		
      sql.close()
   } 
}

假設您只想根據條件選擇某些行。以下程式碼演示如何新增引數佔位符來搜尋值。上述示例也可以編寫為採用引數,如下面的程式碼所示。$ 符號用於定義引數,然後在執行 sql 語句時可以用值替換它。

import java.sql.*; 
import groovy.sql.Sql
 
class Example {
   static void main(String[] args) {
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser', 
         'test123', 'com.mysql.jdbc.Driver')
			
      sql.connection.autoCommit = false  
      
      def firstname = "Mac"
      def lastname ="Mohan"
      def age = 20
      def sex = "M"
      def income = 2000  
		
      def sqlstr = "INSERT INTO EMPLOYEE(FIRST_NAME,LAST_NAME, AGE, SEX, 
         INCOME) VALUES " + "(${firstname}, ${lastname}, ${age}, ${sex}, ${income} )"
			
      try {
         sql.execute(sqlstr);
         sql.commit()
         println("Successfully committed") 
      } catch(Exception ex) {
         sql.rollback()
         println("Transaction rollback")
      }
		
      sql.close()
   }
}

讀取操作

任何資料庫上的讀取操作都意味著從資料庫中獲取一些有用的資訊。一旦建立了資料庫連線,您就可以對該資料庫進行查詢。

讀取操作是透過使用 sql 類的 eachRow 方法執行的。

語法

eachRow(GString gstring, Closure closure) 

執行給定的 SQL 查詢,並使用結果集的每一行呼叫給定的 Closure。

引數

  • Gstring − 需要執行的 sql 語句。

  • Closure − 用於處理從讀取操作中檢索到的行的 closure 語句。執行給定的 SQL 查詢,並使用結果集的每一行呼叫給定的 Closure。

以下程式碼示例演示如何從 employee 表中獲取所有記錄。

import java.sql.*; 
import groovy.sql.Sql
 
class Example {
   static void main(String[] args) {
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser', 
         'test123', 'com.mysql.jdbc.Driver')  
			
      sql.eachRow('select * from employee') {
         tp -> 
         println([tp.FIRST_NAME,tp.LAST_NAME,tp.age,tp.sex,tp.INCOME])
      }  
		
      sql.close()
   } 
}

上述程式的輸出將是:

[Mac, Mohan, 20, M, 2000.0]

更新操作

任何資料庫上的更新操作都意味著更新資料庫中已存在的條記錄。以下過程更新所有 SEX 為 'M' 的記錄。這裡,我們將所有男性的 AGE 增加一年。

import java.sql.*; 
import groovy.sql.Sql 

class Example {
   static void main(String[] args){
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser', 
         'test@123', 'com.mysql.jdbc.Driver')
			
      sql.connection.autoCommit = false
      def sqlstr = "UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = 'M'" 
	  
      try {
         sql.execute(sqlstr);
         sql.commit()
         println("Successfully committed")
      }catch(Exception ex) {
         sql.rollback() 
         println("Transaction rollback")
      }
		
      sql.close()
   } 
}

刪除操作

當您想要從資料庫中刪除某些記錄時,需要執行刪除操作。以下是刪除 EMPLOYEE 表中 AGE 大於 20 的所有記錄的過程。

import java.sql.*; 
import groovy.sql.Sql 

class Example {
   static void main(String[] args) {
      // Creating a connection to the database
      def sql = Sql.newInstance('jdbc:mysql://:3306/TESTDB', 'testuser', 
         'test@123', 'com.mysql.jdbc.Driver')
			
      sql.connection.autoCommit = false
      def sqlstr = "DELETE FROM EMPLOYEE WHERE AGE > 20"
   
      try {
         sql.execute(sqlstr);
         sql.commit()
         println("Successfully committed")
      }catch(Exception ex) {
         sql.rollback()
         println("Transaction rollback")
      }
   
      sql.close()
   } 
}

執行事務

事務是一種確保資料一致性的機制。事務具有以下四個屬性:

  • 原子性 − 事務要麼完全完成,要麼什麼也不發生。

  • 一致性 − 事務必須從一致狀態開始,並使系統保持一致狀態。

  • 隔離性 − 事務的中間結果在當前事務之外不可見。

  • 永續性 − 一旦事務提交,其效果將是持久的,即使在系統故障後也是如此。

這是一個關於如何實現事務的簡單示例。我們已經在之前的刪除操作主題中看到了這個示例。

def sqlstr = "DELETE FROM EMPLOYEE WHERE AGE > 20" 
 
try {
   sql.execute(sqlstr); 
   sql.commit()
   println("Successfully committed") 
}catch(Exception ex) {
   sql.rollback()
   println("Transaction rollback") 
} 
sql.close()

提交操作

提交操作會告訴資料庫繼續執行操作並最終確定對資料庫的所有更改。

在上面的示例中,這是透過以下語句實現的:

sql.commit()

回滾操作

如果您對一個或多個更改不滿意,並且想要完全撤消這些更改,則可以使用回滾方法。在上面的示例中,這是透過以下語句實現的:

sql.rollback()

斷開資料庫連線

要斷開資料庫連線,請使用 close 方法。

sql.close()

Groovy - 構建器

在軟體開發過程中,開發人員有時會花費大量時間建立資料結構、領域類、XML、GUI 佈局、輸出流等。有時,用於建立這些特定需求的程式碼會導致在許多地方重複相同的程式碼片段。這就是 Groovy 構建器發揮作用的地方。Groovy 具有可用於建立標準物件和結構的構建器。這些構建器節省了時間,因為開發人員無需編寫自己的程式碼來建立這些構建器。在本節中,我們將瞭解 Groovy 中可用的不同構建器。

Swing 構建器

在 Groovy 中,還可以使用 Groovy 中提供的 Swing 構建器建立圖形使用者介面。開發 Swing 元件的主要類是 SwingBuilder 類。此類具有許多用於建立圖形元件的方法,例如:

  • JFrame − 用於建立框架元素。

  • JTextField − 用於建立文字欄位元件。

讓我們來看一個使用 SwingBuilder 類建立 Swing 應用程式的簡單示例。在下面的示例中,您可以看到以下幾點:

  • 您需要匯入 groovy.swing.SwingBuilder 和 javax.swing.* 類。

  • Swing 應用程式中顯示的所有元件都是 SwingBuilder 類的一部分。

  • 對於框架本身,您可以指定框架的初始位置和大小。您還可以指定框架的標題。

  • 您需要將 Visibility 屬性設定為 true,才能顯示框架。

import groovy.swing.SwingBuilder 
import javax.swing.* 

// Create a builder 
def myapp = new SwingBuilder()

// Compose the builder 
def myframe = myapp.frame(title : 'Tutorials Point', location : [200, 200], 
   size : [400, 300], defaultCloseOperation : WindowConstants.EXIT_ON_CLOSE {         
      label(text : 'Hello world')
   } 
	
// The following  statement is used for displaying the form 
frame.setVisible(true)

上述程式的輸出如下所示。以下輸出顯示了一個 JFrame 和一個帶有“Hello World”文字的 JLabel。

JLabel With a Text

讓我們來看下一個建立帶有文字框的輸入螢幕的示例。在下面的示例中,我們想要建立一個包含學生姓名、科目和學校名稱文字框的表單。在下面的示例中,您可以看到以下關鍵點:

  • 我們正在為螢幕上的控制元件定義佈局。在這種情況下,我們使用的是網格佈局。
  • 我們正在為標籤使用對齊屬性。
  • 我們正在使用 textField 方法在螢幕上顯示文字框。
import groovy.swing.SwingBuilder 
import javax.swing.* 
import java.awt.*
 
// Create a builder 
def myapp = new SwingBuilder() 

// Compose the builder 
def myframe = myapp.frame(title : 'Tutorials Point', location : [200, 200], 
   size : [400, 300], defaultCloseOperation : WindowConstants.EXIT_ON_CLOSE) { 
      panel(layout: new GridLayout(3, 2, 5, 5)) { 
         label(text : 'Student Name:', horizontalAlignment : JLabel.RIGHT) 
         textField(text : '', columns : 10) 
			
         label(text : 'Subject Name:', horizontalAlignment : JLabel.RIGHT) 
         textField(text : '', columns : 10)
			
         label(text : 'School Name:', horizontalAlignment : JLabel.RIGHT) 
         textField(text : '', columns : 10) 
      } 
   } 
	
// The following  statement is used for displaying the form 
myframe.setVisible(true)

上面程式的輸出如下:

Display Form

事件處理程式

現在讓我們來看一下事件處理程式。事件處理程式用於按鈕,以便在按下按鈕時執行某種處理。每個按鈕偽方法呼叫都包含 actionPerformed 引數。這表示一個作為閉包呈現的程式碼塊。

讓我們來看下一個建立帶有 2 個按鈕的螢幕的示例。按下任一按鈕時,都會向控制檯螢幕傳送相應的郵件。在下面的示例中,您可以看到以下關鍵點:

  • 對於每個定義的按鈕,我們都使用 actionPerformed 方法並定義一個閉包,以便在單擊按鈕時向控制檯傳送一些輸出。

import groovy.swing.SwingBuilder 
import javax.swing.* 
import java.awt.* 

def myapp = new SwingBuilder()
  
def buttonPanel = {
   myapp.panel(constraints : BorderLayout.SOUTH) {
	
      button(text : 'Option A', actionPerformed : {
         println 'Option A chosen'
      })
		
      button(text : 'Option B', actionPerformed : {
         println 'Option B chosen'
      })
   }
}
  
def mainPanel = {
   myapp.panel(layout : new BorderLayout()) {
      label(text : 'Which Option do you want', horizontalAlignment : 
      JLabel.CENTER,
      constraints : BorderLayout.CENTER)
      buttonPanel()
   }
}
  
def myframe = myapp.frame(title : 'Tutorials Point', location : [100, 100],
   size : [400, 300], defaultCloseOperation : WindowConstants.EXIT_ON_CLOSE){
      mainPanel()
   }
	
myframe.setVisible(true)

上述程式的輸出如下所示。當您單擊任一按鈕時,所需的郵件將傳送到控制檯日誌螢幕。

Option Button

上述示例的另一個變體是定義可以作為處理程式的方法。在下面的示例中,我們定義了 DisplayA 和 DisplayB 的 2 個處理程式。

import groovy.swing.SwingBuilder 
import javax.swing.* 
import java.awt.* 

def myapp = new SwingBuilder()
  
def DisplayA = {
   println("Option A") 
} 

def DisplayB = {
   println("Option B")
}

def buttonPanel = {
   myapp.panel(constraints : BorderLayout.SOUTH) {
      button(text : 'Option A', actionPerformed : DisplayA) 
      button(text : 'Option B', actionPerformed : DisplayB)
   }
}  

def mainPanel = {
   myapp.panel(layout : new BorderLayout()) {
      label(text : 'Which Option do you want', horizontalAlignment : JLabel.CENTER,
      constraints : BorderLayout.CENTER)
      buttonPanel()
   }
}  

def myframe = myapp.frame(title : 'Tutorials Point', location : [100, 100],
   size : [400, 300], defaultCloseOperation : WindowConstants.EXIT_ON_CLOSE) {
      mainPanel()
   } 
	
myframe.setVisible(true) 

上述程式的輸出將與之前的示例相同。

DOM 構建器

DOM 構建器可用於解析 HTML、XHTML 和 XML 並將其轉換為 W3C DOM 樹。

以下示例演示如何使用 DOM 構建器。

String records = '''
   <library>
	
      <Student>
         <StudentName division = 'A'>Joe</StudentName>
         <StudentID>1</StudentID>
      </Student>
	  
      <Student>
         <StudentName division = 'B'>John</StudentName>
         <StudentID>2</StudentID>
      </Student>
	  
      <Student>
         <StudentName division = 'C'>Mark</StudentName>
         <StudentID>3</StudentID>
      </Student>
		
   </library>'''
   
def rd = new StringReader(records) 
def doc = groovy.xml.DOMBuilder.parse(rd)

JsonBuilder

JsonBuilder 用於建立 json 型別物件。

以下示例演示如何使用 Json 構建器。

def builder = new groovy.json.JsonBuilder() 

def root = builder.students {
   student {
      studentname 'Joe'
      studentid '1'
		
      Marks(
         Subject1: 10,
         Subject2: 20,
         Subject3:30,
      )
   } 
} 
println(builder.toString());

上述程式的輸出如下所示。輸出清楚地表明 Jsonbuilder 能夠根據結構化的節點集構建 json 物件。

{"students":{"student":{"studentname":"Joe","studentid":"1","Marks":{"Subject1":10,
"S ubject2":20,"Subject3":30}}}}

jsonbuilder 還可以接收列表並將其轉換為 json 物件。以下示例演示瞭如何實現這一點。

def builder = new groovy.json.JsonBuilder() 
def lst = builder([1, 2, 3]) 
println(builder.toString());

上述程式的輸出如下所示。

[1,2,3]

jsonBuilder 也可用於類。以下示例演示瞭如何將類的物件作為 json 構建器的輸入。

def builder = new groovy.json.JsonBuilder() 

class Student {
   String name  
} 

def studentlist = [new Student (name: "Joe"), new Student (name: "Mark"), 
   new Student (name: "John")] 
	
builder studentlist, { Student student ->name student.name} 
println(builder)

上述程式的輸出如下所示。

[{"name":"Joe"},{"name":"Mark"},{"name":"John"}] 

NodeBuilder

NodeBuilder 用於建立 Node 物件的巢狀樹,以處理任意資料。Nodebuilder 用法的示例如下所示。

def nodeBuilder = new NodeBuilder() 

def studentlist = nodeBuilder.userlist {
   user(id: '1', studentname: 'John', Subject: 'Chemistry')
   user(id: '2', studentname: 'Joe', Subject: 'Maths')
   user(id: '3', studentname: 'Mark', Subject: 'Physics') 
} 

println(studentlist)

FileTreeBuilder

FileTreeBuilder 是一個構建器,用於根據規範生成檔案目錄結構。以下是 FileTreeBuilder 用法示例。

tmpDir = File.createTempDir() 
def fileTreeBuilder = new FileTreeBuilder(tmpDir) 

fileTreeBuilder.dir('main') {
   dir('submain') {
      dir('Tutorial') {
        file('Sample.txt', 'println "Hello World"')
      }
   } 
}

執行上述程式碼後,將在 main/submain/Tutorial 資料夾中建立一個名為 sample.txt 的檔案。sample.txt 檔案將包含文字“Hello World”。

Groovy - 命令列

稱為 groovysh 的 Groovy shell 可輕鬆用於評估 Groovy 表示式、定義類和執行簡單的程式。Groovy 安裝時會安裝命令列 shell。

以下是 Groovy 中可用的命令列選項:

命令列引數 完整名稱 詳細資訊
-C --color[=FLAG] 啟用或停用 ANSI 顏色
-D --define=NAME=VALUE 定義系統屬性
-T --terminal=TYPE 指定要使用的終端 TYPE
-V --version 顯示版本
-classpath 指定查詢類檔案的位置 - 必須是第一個引數
-cp --classpath “-classpath”的別名
-d --debug 啟用除錯輸出
-e --evaluate=arg 啟動互動式會話時首先評估選項
-h --help 顯示此幫助訊息
-q --quiet 抑制多餘的輸出
-v --verbose 啟用詳細輸出

以下快照顯示了在 Groovy shell 中執行表示式的簡單示例。在下面的示例中,我們只是在 Groovy shell 中列印“Hello World”。

Groovy Shell

類和函式

在命令提示符下定義類、建立新物件並在類上呼叫方法非常容易。以下示例演示瞭如何實現這一點。在下面的示例中,我們正在建立一個簡單的 Student 類和一個簡單的方法。在命令提示符本身中,我們正在建立該類的物件並呼叫 Display 方法。

Create Std Class

在命令提示符下定義方法並呼叫方法非常容易。請注意,該方法是使用 def 型別定義的。另請注意,我們包含了一個名為 name 的引數,然後在呼叫 Display 方法時用實際值替換它。以下示例演示瞭如何實現這一點。

prompt and invoke command

命令

Shell擁有許多不同的命令,這些命令提供了對Shell環境的豐富訪問許可權。以下是這些命令及其功能的列表。

序號 命令 &smp; 命令描述
1

:help

(:h ) 顯示此幫助資訊

2

?

(:? ) :help 的別名

3

:exit

(:x ) 退出Shell

4

:quit

(:q ) :exit 的別名

5

import

(:i ) 將類匯入名稱空間

6

:display

(:d ) 顯示當前緩衝區

7

:clear

(:c ) 清除緩衝區並重置提示符計數器

8

:show

(:S ) 顯示變數、類或匯入

9

:inspect

(:n ) 使用GUI物件瀏覽器檢查變數或最後一個結果

10

:purge

(:p ) 清除變數、類、匯入或首選項

11

:edit

(:e ) 編輯當前緩衝區

12

:load

(:l ) 將檔案或URL載入到緩衝區

13

.

(:. ) :load 的別名

14

.save

(:s ) 將當前緩衝區儲存到檔案

15

.record

(:r ) 將當前會話記錄到檔案

16

:alias

(:a ) 建立別名

17

:set

(:= ) 設定(或列出)首選項

18

:register

(:rc) 在Shell中註冊一個新命令

19

:doc

(:D ) 開啟瀏覽器視窗,顯示引數的文件

20

:history

(:H ) 顯示、管理和調回編輯行歷史記錄

Groovy - 單元測試

面向物件系統最基本的單元是類。因此,單元測試包括在類內進行測試。所採用的方法是建立一個被測試類的物件,並使用它來檢查所選方法是否按預期執行。並非每種方法都可以測試,因為並非總是實際測試每一個東西。但是應該對關鍵和重要的方法進行單元測試。

JUnit是一個開源測試框架,是Java程式碼自動化單元測試的業界公認標準。幸運的是,JUnit框架可以很容易地用於測試Groovy類。只需要擴充套件Groovy環境一部分的GroovyTestCase類即可。Groovy測試用例類基於JUnit測試用例。

編寫簡單的JUnit測試用例

假設我們在應用程式類檔案中定義了以下類:

class Example {
   static void main(String[] args) {
      Student mst = new Student();
      mst.name = "Joe";
      mst.ID = 1;
      println(mst.Display())
   } 
} 
 
public class Student {
   String name;
   int ID;
	
   String Display() {
      return name +ID;
   }  
}

上述程式的輸出如下所示。

Joe1

現在假設我們想為Student類編寫一個測試用例。一個典型的測試用例如下所示。關於以下程式碼,需要注意以下幾點:

  • 測試用例類擴充套件了GroovyTestCase類
  • 我們使用assert語句來確保Display方法返回正確的字串。
class StudentTest extends GroovyTestCase {
   void testDisplay() {
      def stud = new Student(name : 'Joe', ID : '1')
      def expected = 'Joe1'
      assertToString(stud.Display(), expected)
   }
}

Groovy測試套件

通常,隨著單元測試數量的增加,逐個執行所有測試用例會變得越來越困難。因此,Groovy提供了一種建立測試套件的功能,可以將所有測試用例封裝到一個邏輯單元中。以下程式碼片段顯示瞭如何實現這一點。關於這段程式碼,需要注意以下幾點:

  • GroovyTestSuite用於將所有測試用例封裝到一個單元中。

  • 在下面的示例中,我們假設我們有兩個測試用例檔案,一個名為**StudentTest**,另一個名為**EmployeeTest**,其中包含所有必要的測試。

import groovy.util.GroovyTestSuite 
import junit.framework.Test 
import junit.textui.TestRunner 

class AllTests { 
   static Test suite() { 
      def allTests = new GroovyTestSuite() 
      allTests.addTestSuite(StudentTest.class) 
      allTests.addTestSuite(EmployeeTest.class) 
      return allTests 
   } 
} 

TestRunner.run(AllTests.suite())

Groovy - 模板引擎

Groovy的模板引擎的工作方式類似於郵件合併(從資料庫中自動新增姓名和地址到信函和信封中,以便於向許多地址傳送郵件,特別是廣告),但它更通用。

字串中的簡單模板

如果我們採用下面的簡單示例,我們首先定義一個名為name的變數來儲存字串“Groovy”。在println語句中,我們使用$符號來定義一個引數或模板,其中可以插入值。

def name = "Groovy" 
println "This Tutorial is about ${name}"

如果在Groovy中執行上述程式碼,將顯示以下輸出。輸出清楚地顯示$name被def語句分配的值替換了。

簡單的模板引擎

以下是一個SimpleTemplateEngine的示例,它允許您在模板中使用類似JSP的指令碼程式和EL表示式來生成引數化文字。模板引擎允許您繫結引數列表及其值,以便可以在包含已定義佔位符的字串中替換它們。

def text ='This Tutorial focuses on $TutorialName. In this tutorial you will learn 

about $Topic'  

def binding = ["TutorialName":"Groovy", "Topic":"Templates"]  
def engine = new groovy.text.SimpleTemplateEngine() 
def template = engine.createTemplate(text).make(binding) 

println template

如果在Groovy中執行上述程式碼,將顯示以下輸出。

現在讓我們將模板功能用於XML檔案。第一步,讓我們將以下程式碼新增到名為Student.template的檔案中。在下面的檔案中,您會注意到我們沒有為元素新增實際值,而是佔位符。因此,$name、$is和$subject都是作為佔位符新增的,需要在執行時替換。

<Student> 
   <name>${name}</name> 
   <ID>${id}</ID> 
   <subject>${subject}</subject> 
</Student>

現在讓我們新增Groovy指令碼程式碼來新增可用於用實際值替換上述模板的功能。關於以下程式碼,需要注意以下幾點:

  • 佔位符到實際值的對映是透過繫結和SimpleTemplateEngine完成的。繫結是一個Map,其中佔位符作為鍵,替換作為值。

import groovy.text.* 
import java.io.* 

def file = new File("D:/Student.template") 
def binding = ['name' : 'Joe', 'id' : 1, 'subject' : 'Physics']
				  
def engine = new SimpleTemplateEngine() 
def template = engine.createTemplate(file) 
def writable = template.make(binding) 

println writable

如果在Groovy中執行上述程式碼,將顯示以下輸出。從輸出可以看出,值已成功替換到相關的佔位符中。

<Student> 
   <name>Joe</name> 
   <ID>1</ID> 
   <subject>Physics</subject> 
</Student>

StreamingTemplateEngine

StreamingTemplateEngine引擎是Groovy中提供的另一個模板引擎。這與SimpleTemplateEngine類似,但使用可寫閉包建立模板,使其更易於擴充套件大型模板。具體來說,此模板引擎可以處理大於64k的字串。

以下是StreamingTemplateEngine使用方法的示例:

def text = '''This Tutorial is <% out.print TutorialName %> The Topic name 

is ${TopicName}''' 
def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)
  
def binding = [TutorialName : "Groovy", TopicName  : "Templates",]
String response = template.make(binding) 
println(response)

如果在Groovy中執行上述程式碼,將顯示以下輸出。

This Tutorial is Groovy The Topic name is Templates

XMLTemplateEngine

XmlTemplateEngine用於模板場景,其中模板源和預期輸出都應為XML。模板使用正常的${expression}和$variable表示法將任意表達式插入模板。

以下是XMLTemplateEngine使用方法的示例。

def binding = [StudentName: 'Joe', id: 1, subject: 'Physics'] 
def engine = new groovy.text.XmlTemplateEngine() 

def text = '''\
   <document xmlns:gsp='http://groovy.codehaus.org/2005/gsp'>
      <Student>
         <name>${StudentName}</name>
         <ID>${id}</ID>
         <subject>${subject}</subject>
      </Student>
   </document> 
''' 

def template = engine.createTemplate(text).make(binding) 
println template.toString()

如果在Groovy中執行上述程式碼,將顯示以下輸出

   Joe
    
    
   1
    
    
   Physics 

Groovy - 元物件程式設計

元物件程式設計或MOP可用於動態呼叫方法,以及動態建立類和方法。

這是什麼意思呢?讓我們考慮一個名為Student的類,它是一個沒有任何成員變數或方法的空類。假設您必須在這個類上呼叫以下語句。

Def myStudent = new Student() 
myStudent.Name = ”Joe”; 
myStudent.Display()

現在在元物件程式設計中,即使該類沒有Name成員變數或Display()方法,上述程式碼仍然可以工作。

這怎麼可能呢?為此,必須實現GroovyInterceptable接口才能連線到Groovy的執行過程。以下是此介面可用的方法。

Public interface GroovyInterceptable { 
   Public object invokeMethod(String methodName, Object args) 
   Public object getproperty(String propertyName) 
   Public object setProperty(String propertyName, Object newValue) 
   Public MetaClass getMetaClass() 
   Public void setMetaClass(MetaClass metaClass) 
}

因此,在上述介面描述中,假設您必須實現invokeMethod(),它將被呼叫,無論方法是否存在。

缺少屬性

讓我們來看一個如何為缺少的屬性實現元物件程式設計的示例。關於以下程式碼,需要注意以下幾點:

  • Student類沒有定義名為Name或ID的成員變數。

  • Student類實現了GroovyInterceptable介面。

  • 有一個名為dynamicProps的引數,它將用於儲存動態建立的成員變數的值。

  • 已實現getproperty和setproperty方法,用於在執行時獲取和設定類的屬性值。

class Example {
   static void main(String[] args) {
      Student mst = new Student();
      mst.Name = "Joe";
      mst.ID = 1;
		
      println(mst.Name);
      println(mst.ID);
   }
}

class Student implements GroovyInterceptable { 
   protected dynamicProps=[:]
	
   void setProperty(String pName,val) {
      dynamicProps[pName] = val
   }
   
   def getProperty(String pName) {
      dynamicProps[pName]
   } 
} 

以下程式碼的輸出將是:

Joe 
1

缺少方法

讓我們來看一個如何為缺少的屬性實現元物件程式設計的示例。關於以下程式碼,需要注意以下幾點:

  • Student類現在實現了invokeMethod方法,無論方法是否存在,該方法都會被呼叫。

class Example {
   static void main(String[] args) {
      Student mst = new Student();
      mst.Name = "Joe";
      mst.ID = 1;
		
      println(mst.Name);
      println(mst.ID);
      mst.AddMarks();
   } 
}
 
class Student implements GroovyInterceptable {
   protected dynamicProps = [:]  
    
   void setProperty(String pName, val) {
      dynamicProps[pName] = val
   } 
   
   def getProperty(String pName) {
      dynamicProps[pName]
   }
   
   def invokeMethod(String name, Object args) {
      return "called invokeMethod $name $args"
   }
}

以下將顯示以下程式碼的輸出。請注意,即使Display方法不存在,也不會出現缺少方法異常。

Joe 
1 

元類

此功能與MetaClass實現相關。在預設實現中,您可以訪問欄位而無需呼叫其getter和setter。以下示例顯示瞭如何使用metaClass函式來更改類中私有變數的值。

class Example {
   static void main(String[] args) {
      Student mst = new Student();
      println mst.getName()
      mst.metaClass.setAttribute(mst, 'name', 'Mark')
      println mst.getName()
   } 
} 

class Student {
   private String name = "Joe";
	
   public String getName() {
      return this.name;
   } 
}

以下程式碼的輸出將是:

Joe 
Mark

方法缺失

Groovy支援methodMissing的概念。此方法與invokeMethod的不同之處在於,它僅在方法分派失敗時呼叫,即找不到給定名稱和/或給定引數的任何方法時。以下示例顯示瞭如何使用methodMissing。

class Example {
   static void main(String[] args) {
      Student mst = new Student();
      mst.Name = "Joe";
      mst.ID = 1;
		
      println(mst.Name);
      println(mst.ID);
      mst.AddMarks();
   } 
} 

class Student implements GroovyInterceptable {
   protected dynamicProps = [:]  
    
   void setProperty(String pName, val) {
      dynamicProps[pName] = val
   }
   
   def getProperty(String pName) {
      dynamicProps[pName]
   }
   
   def methodMissing(String name, def args) {         
      println "Missing method"
   }  
}

以下程式碼的輸出將是:

Joe 
1 
Missing method 
廣告