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 的官方网站是 https://groovy-lang.cn/

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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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://127.0.0.1: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 
广告