【新手入门】Groovy基础语法
Groovy
1. Groovy概述
Groovy概述
Apache Groovy 编程语言 (groovy-lang.org)
Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
Groovy是从Java衍生出来的,并且运行在Java虚拟机上的语言.其目标是不管作为脚本语言,还是编程语言,都可以简单、直接使用。Groovy 也并不会替代 Java,而是相辅相成、互补的关系,具体使用哪门语言这取决于要解决的问题和使用的场景。
为什么要学习Groovy
- Groovy基于JVM,这使我能够调用产品的Java代码,也能够调用Java标准库里的代码。除些之外,还可以通过Maven或Gradle使用大量的第三方Java库。
- Groovy是动态语言,扩展了Java的容器类,提供了完善的函数式编程和元编程支持。这让我们可以写出简洁而富有表现力的代码。
- Groovy提供了大量的语法糖。与Java自身繁冗的代码相比,这些语法糖大大节约了我们编写脚本的时间,减少了我的脚本的代码量。
2. Groovy环境
官网下载地址:https://groovy.apache.org/download.html
下载
解压
配置环境变量
- 在
Path
环境变量中添加 Groovy 的bin 目录路径
验证
启动命令窗口,输入groovy -version指令,打印如下图,即为安装成功。
集成IDEA
创建项目
新建Project,选择Groovy,注意要选择Project SDK和Groovy library,默认是没有Groovy library的,选择右边的 create 按钮 ,之后选择自己安装groovy的目录即可,然后点击next
新建测试:
1 | public class Groovy_H { |
3. Groovy语法
3.1 Hello World
1 | class Groovy_T { |
- 使用 println 就可打印输出,并且类和方法默认就是public,可以不用写
- Groovy注释标记和Java一样,支持
//
或者/**/
- Groovy语句可以不用分号结尾
3.2 数据类型
Groovy 的内置数据类型和 Java 一样有8种。byte、short、int、long、float、double、char、boolean
并且都有其对应的封装类
此外,以下类可用于支持高精度计算 -
名称 | 描述 | 例如 |
---|---|---|
java.math.BigInteger | 不可变的任意精度的有符号整数数字 | 30克 |
java.math.BigDecimal | 不可变的任意精度的有符号十进制数 | 3.5克 |
3.3 Groovy 变量
Groovy中的变量可以通过两种方式定义 - 使用数据类型的本地语法,或者使用def关键字。对于变量定义,必须明确提供类型名称或在替换中使用”def”。这是Groovy解析器需要的。
Groovy 提供了def
关键字供使用,它可以省略变量类型的定义,根据变量的值进行类型推导。
变量声明
变量声明告诉编译器为变量创建存储的位置和大小。
下面是一个变量声明的例子 -
1 | class Example { |
3.4 Groovy 字符串
Groovy提供了多种表示String字面量的方法。 Groovy中的字符串可以用单引号('),双引号(“)或三引号(”“”)
括起来。
- 单引号
''
–>所见即所得,’不支持’变量内插,有’特殊字符’同样的通过’反斜杠转义’ - 双引号
""
–>可以通过${var}
进行”变量内插(GString)” –>’常用 - 三引号
"""
–>可以改变输出格式
GStrings
是groovy.lang.GString的实例,并且允许文本中包含占位符,GStrings并不是String的子类,因为String类是最终类(final class)不能被继承。然而,GString与一般的字符串一样,因为Groovy能将GStrings转型为Java strings。
GString 适用于编写模板代码,因为必须动态构建字符串,上面的字符串拼接可以优化为:
1 |
|
3.4 Groovy 运算符
运算符是一个符号,通知编译器执行特定的数学或逻辑操作。
大部分运算符和 Java 一样,如:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符
与 Java 不同点在于,新增了 范围运算符
Groovy支持范围的概念,并在..符号的帮助下提供范围运算符的符号。下面给出了范围运算符的一个简单示例。
1 | def range = 0..5 |
这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为0和上限为5。
以下代码段显示了如何使用各种运算符。
1 | class Example { |
当我们运行上面的程序,我们会得到以下结果 -
从println语句中,可以看到显示在range语句中定义的整个数字范围。
get语句用于从定义的范围中获取一个对象,它将索引值作为参数。
1 | [5, 6, 7, 8, 9, 10] |
3.5 Groovy 循环
while循环
while语句首先通过计算条件表达式(布尔值)来执行,如果结果为真,则执行while循环中的语句。
1 | class Example { |
通过首先计算条件表达式(布尔值)来执行 while 语句,如果结果为true,则执行while循环中的语句。从while语句中的条件的评估开始重复该过程 此循环继续,直到条件计算为false。当条件变为假时,循环终止。 然后程序逻辑继续紧跟在while语句之后的语句
for循环
for语句用于遍历一组值。
1 | class Example { |
for-in循环
1 | class Example { |
在上面的例子中,我们首先初始化一个具有0,1,2和3的4个值的整数数组。然后我们使用for循环语句首先定义一个变量i,然后遍历数组中的所有整数 并相应地打印值。
for-in 语句也可用于循环范围。以下示例说明如何完成此操作:
1 | class Example { |
for-in 语句也可用于循环访问Map
1 | class Example { |
3.6 Groovy 方法
Groovy 中的方法是使用返回类型或使用 def 关键字定义的。方法可以接收任意数量的参数。定义参数时,不必显式定义类型。可以添加修饰符,如 public,private 和 protected。默认情况下,如果未提供可见性修饰符,则该方法为 public。
最简单的方法是没有参数的方法,如下所示:
1 | def methodName() { |
下面是一个简单方法的例子
1 | class Example { |
在上面的例子中,DisplayName 是一个简单的方法,它由两个 println 语句组成,用于向控制台输出一些文本。在我们的静态 main 方法中,我们只是调用 DisplayName 方法。上述方法的输出将是 -
1 | This is how methods work in groovy |
方法参数
如果一个方法的行为由一个或多个参数的值确定,则它通常是有用的。我们可以使用方法参数将值传递给被调用的方法。请注意,参数名称必须彼此不同。
使用参数的最简单的方法类型,如下所示 −
1 | def methodName(parameter1, parameter2, parameter3) { |
以下是使用参数的简单方法的示例
1 | class Example { |
在这个例子中,我们创建一个带有 2 个参数 a 和 b 的 sum 方法。两个参数都是 int 类型。然后我们从我们的 main 方法中调用 sum 方法,并将值传递给变量 a 和 b。
然后我们从我们的 main 方法中调用 sum 方法,并将值传递给变量 a 和 b。
上述方法的输出将是值 15。
默认参数
Groovy 中还有一个规定来指定方法中的参数的默认值。 如果没有值传递给参数的方法,则使用缺省值。 如果使用非默认和默认参数,则必须注意,默认参数应在参数列表的末尾定义。
以下是使用参数的简单方法的示例 -
1 | def someMethod(parameter1, parameter2 = 0, parameter3 = 0) { |
让我们看看我们之前看到的添加两个数字的相同示例,并创建一个具有一个默认和另一个非默认参数的方法 -
1 | class Example { |
在这个例子中,我们创建一个具有两个参数 a 和 b 的 sum 方法。两个参数都是 int 类型。此示例和上一个示例的区别在于,在这种情况下,我们将 b 的默认值指定为5。 因此,当我们从 main 方法中调用 sum 方法时,我们可以选择只传递一个值为6的值,并将其分配给 sum 方法中的参数 a。
上述方法的输出将为值 11。
1 | class Example { |
我们也可以通过传递 2 个值来调用 sum 方法,在上面的例子中,我们传递 2 个值 6 第二个值 6 实际上将替换分配给参数 b 的默认值。
上述方法的输出将是值 12。
方法返回值
方法也可以将值返回到调用程序。 这在现在编程语言中是必需的,其中方法执行某种计算,然后将所需值返回到调用方法。
下面是一个带有返回值的简单方法的例子。
1 | class Example { |
在我们上面的例子中,注意这次我们为我们的方法 sum 指定一个类型为 int 的返回类型。 在方法中,我们使用 return 语句将 sum 值发送到调用主程序。 由于方法的值现在可用于 main 方法,因此我们使用 println 函数在控制台中显示该值。
在前面的例子中,我们将我们的方法定义为静态方法,这意味着我们可以直接从类中访问这些方法。方法的下一个示例是实例方法,其中通过创建类的对象来访问方法。我们将在后面的章节中看到类,现在我们将演示如何使用方法。
上述方法的输出将为值 11。
实例方法
方法通常在 Groovy 中的类中实现,就像 Java 语言一样。类只是一个蓝图或模板,用于创建定义其属性和行为的不同对象。类对象显示由其类定义的属性和行为。因此,通过在类中创建方法来定义行为。
以下是如何实现方法的示例。
1 | class Example { |
在我们上面的例子中,这次我们没有为类方法指定静态属性。在我们的 main 函数中,我们实际上创建了一个 Example 类的实例,然后调用 ‘ex’ 对象的方法。
上述方法的输出将是值 100。
本地和外部参数名称
Groovy 提供的设施就像java一样具有本地和全局参数。在下面的示例中,lx 是一个局部参数,它只具有 getX() 函数内的作用域,x 是一个全局属性,可以在整个 Example 类中访问。如果我们尝试访问 getX() 函数之外的变量 lx,我们将得到一个错误。
1 | class Example { |
当我们运行上面的程序,我们会得到以下结果。
1 | 200 |
3.7 Groovy 列表
集合定义
定义一个列表集合的方式有点像 Java 中的定义数组一样,默认的类型就是 ArrayList
1 | //1. 在 Groovy 中定义的集合默认就是对应于 Java 中 ArrayList 集合 |
获取指定角标下的元素
可以通过角标访问集合指定位置的元素,正数角标是从0位置左往右算起,负数角标是从0位置往反方向算。
下面的代码片段中出现的负数角标,就有别于 JAVA ,因为在 JAVA 中出现负数角标,基本就会报异常了,0 就是第一个位置的元素,-1就是最后一个位置的元素,一次类推即可
1 | def list = [1,2,3,4,5] |
获取指定范围的元素
list[index1..index2]取出指定范围的元素
1 | def list = [1,2,3,4,5] |
添加元素
在列表集合中添加元素的方式有以下三种
1 | def list = [1, 2, 3, 4, 5] |
移除元素
1 | def list = [1, 2, 3, 4, 5] |
元素遍历
在
Groovy
中使用each
来遍历集合,在遍历时,可以选择是否带有角标来选择不同的遍历方法
1 | def list = [1, 2, 3, 4, 5] |
3.8 Groovy 映射
映射(也称为关联数组,字典,表和散列)是对象引用的无序集合。Map集合中的元素由键值访问。 Map中使用的键可以是任何类。当我们插入到Map集合中时,需要两个值:键和值。
Map定义
Map 集合的定义有别于 Java 的定义方式,格式如下
def map = [key1:value1,key2:value2,...]
Groovy 中定义的 Map 默认类型是 java.util.LinkedHashMap
1 | def map = [name: "张三", age: 18] |
获取元素值
Map 集合中指定 key 下的值有有两种方式
1 | def map = [name: "张三", age: 25] |
添加元素
有两种方式可以添加map的值
1 | def map = [name: "张三", age: 25] |
Map 遍历
跟
List
集合一样,Map
集合的遍历也是使用each
方法来实现。
1 | def map = [name: "张三", age: 25] |
3.9 Groovy 面向对象
在Groovy中,如在任何其他面向对象语言中一样,存在类和对象的概念以表示编程语言的对象定向性质。Groovy类是数据的集合和对该数据进行操作的方法。在一起,类的数据和方法用于表示问题域中的一些现实世界对象。
Groovy中的类声明了该类定义的对象的状态(数据)和行为。因此,Groovy类描述了该类的实例字段和方法。
以下是Groovy中的一个类的示例。类的名称是Student,它有两个字段 - StudentID和StudentName。在main函数中,我们创建一个这个类的对象,并将值分配给对象的StudentID和StudentName。
1 | class Student { |
getter和setter方法
在任何编程语言中,总是使用private关键字隐藏实例成员,而是提供getter和setter方法来相应地设置和获取实例变量的值。以下示例显示如何完成此操作。
1 | class Student { |
当我们运行上面的程序,我们将得到以下结果 -
1 | 1 |
请注意以下关于上述程序的要点 -
- 在类中,studentID和studentName都标记为private,这意味着无法从类外部访问它们。
- 每个实例成员都有自己的getter和setter方法。getter方法返回实例变量的值,例如方法int getStudentID()和setter方法设置实例ID的值,例如method - void setStudentName(String pName)
实例方法
在类中包含更多的方法通常是一个很自然的事情,它实际上为类实现了一些功能。在我们的学生示例中,让我们添加Marks1,Marks2和Marks3的实例成员,以表示学生在3个科目中的标记。然后我们将添加一个新的实例方法,计算学生的总分。以下是代码的外观。
在下面的示例中,Total方法是一个额外的Instance方法,它内置了一些逻辑。
1 | class Student { |
当我们运行上面的程序,我们将得到以下结果 -
1 | 60 |
继承
继承可以定义为一个类获取另一个类的属性(方法和字段)的过程。通过使用继承,信息以分级顺序可管理。
继承其他属性的类称为子类(派生类,子类),属性继承的类称为超类(基类,父类)。
extends是用于继承类的属性的关键字。下面给出了extends关键字的语法。在下面的例子中,我们做了以下事情 -
- 创建一个名为Person的类。这个类有一个名为name的实例成员。
- 创建一个名为Student的类,它从Person类继承。请注意,在Person类中定义的名称实例成员在Student类中继承。
- 在Student类构造函数中,我们调用了基类构造函数。
- 在我们的Student类中,我们添加了2个StudentID和Marks1的实例成员。
1 | class Example { |
当我们运行上面的程序,我们将得到以下结果 -
1 | Joe |
内部类
内部类在另一个类中定义。封闭类可以像往常一样使用内部类。另一方面,内部类可以访问其封闭类的成员,即使它们是私有的。不允许除封闭类之外的类访问内部类。
下面是一个外部和内部类的例子。在下面的例子中,我们做了以下事情 -
- 创建一个名为Outer的类,它将是我们的外部类。
- 在Outer类中定义名为name的字符串。
- 在我们的外类中创建一个内部或嵌套类。
- 请注意,在内部类中,我们可以访问在Outer类中定义的名称实例成员。
1 | class Example { |
当我们运行上面的程序,我们将得到以下结果 -
1 | tom |
抽象类
抽象类表示通用概念,因此,它们不能被实例化,被创建为子类化。他们的成员包括字段/属性和抽象或具体方法。抽象方法没有实现,必须通过具体子类来实现。抽象类必须用抽象关键字声明。抽象方法也必须用抽象关键字声明。
在下面的示例中,请注意,Person类现在是一个抽象类,不能被实例化。还要注意,在抽象类中有一个名为DisplayMarks的抽象方法,没有实现细节。在学生类中,必须添加实现细节。
1 | class Example { |
当我们运行上面的程序,我们将得到以下结果 -
1 | Joe |
接口
接口定义了类需要遵守的契约。接口仅定义需要实现的方法的列表,但是不定义方法实现。需要使用interface关键字声明接口。接口仅定义方法签名。接口的方法总是公开的。在接口中使用受保护或私有方法是一个错误。
以下是groovy中的接口示例。在下面的例子中,我们做了以下事情 -
- 创建一个名为Marks的接口并创建一个名为DisplayMarks的接口方法。
- 在类定义中,我们使用implements关键字来实现接口。 因为我们是实现
- 因为我们正在实现接口,我们必须为DisplayMarks方法提供实现。
1 | class Example { |
当我们运行上面的程序,我们将得到以下结果 -
1 | 10 |
4. 闭包
闭包是Groovy语言的精髓之一,Groovy的闭包大大简化了容器的遍历,提升了代码的可扩展性,使代码更加简洁优雅,闭包在Groovy编程中几乎无处不在。
什么是闭包
闭包(Closure)是很多编程语言中很重要的概念,那么Groovy中闭包是什么,官方定义是“Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量”,简而言之,他说一个匿名的代码块,可以接受参数,有返回值,那么到底是怎么样的
闭包特点
groovy 中的闭包是一个开放的匿名代码块,可以接受参数,返回值可以赋值给变量。
我们通常学习的 lamb 表达式是有一定封闭空间,无法访问闭包的变量
1 | int anser = 42; |
而在 groovy 的闭包是可以访问到外部变量的,虽然这样更自由但是也打破这个封闭空间
1 | int anser = 42; |
如何定义
定义闭的语意 :
{ [closureParameters -> ] statements }
,其中[closureParameters->]代表参数们,多参数用逗号分割,用->
隔开参数与内容,没有参数可以不写->
,其中[]
内是可选的闭包参数,可省略。当闭包带有参数,就需要->
来将参数和闭包体相分离
直接定义
直接用大括号定义就是一个最简单的闭包
1 | def hello = {println "hello world"} |
传递参数
闭包是可以传递参数的,类似于
lamda
表达式的使用
1 | def hello = {x -> println "参数值:${x}"} |
接受不同类型参数
1 | def info = { String name,int age -> name + "," + age} |
使用隐含变量 it
有时候我们看到闭包没有传递参数但是可以使用参数
it
,这是使用到了闭包的隐含参数
1 | {it -> println "she is ${it}" } |
作为一个对象使用
闭包在groovy中是
groovy.lang.Closure
类的实例,这使得闭包可以赋值给变量或字段。
1 | def closure = {println "hello world"} |
闭包的调用
直接调用
上面我们已经了解到了如何调用闭包,最简单的方式就是直接调用闭包加上一个括号就可以调用了,括号里面可以传递闭包参数
1 | def hello = {println "hello world"} |
参数传递
闭包是可以传递参数的,如果有参数是可以通过括号进行传递的,如果多个参数可以在后面加上逗号
1 | def hello = {x -> println "参数值:${x}"} |
闭包返回结果
因为闭包是一个函数,所以可以直接调用就可以获取返回结果
1 | def sqrt = { x -> return x * x } |
3.3.4 通过call调用
上面都是通过直接函数调用来进行调用,还可以通过
call
来进行调用
1 | def sqrt = { x -> return x * x } |
3.3.5 闭包的参数
- 参数是可以有类型也可以不定义类型
- 有一个隐含参数 it
- 接受可变参数
1 | def greeting = { String ...name -> "hey ${name.join(" ")}" } |
5. Groovy 文件I/O操作
Groovy在使用I / O时提供了许多辅助方法,Groovy提供了更简单的类来为文件提供以下功能。
- 读取文件
- 写入文件
- 遍历文件树
- 读取和写入数据对象到文件
除此之外,您始终可以使用下面列出的用于文件I / O操作的标准Java类。
- java.io.File
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
使用java代码的基本写法
1 | FileInputStream fin = null; |
对一个文件进行读取的时候,基本上都会用到上面的代码,重复的写这些代码让人感觉很枯燥,同时在阅读这样的代码的时候也极易干扰视线。真正要干的事情也就是把文件内容输出而已。
读取文件
以下示例将输出Groovy中的文本文件的所有行。方法eachLine内置在Groovy中的File类中,目的是确保文本文件的每一行都被读取。
1 | import java.io.File |
File类用于实例化以文件名作为参数的新对象。 然后它接受eachLine的函数,将它放到一个line的变量并相应地打印它。
如果文件包含以下行,它们将被打印。
1 | line : Example1 |
读取文件的内容到字符串
如果要将文件的整个内容作为字符串获取,可以使用文件类的text属性。以下示例显示如何完成此操作。
1 | class Example { |
如果该文件包含以下行,它们将被打印出来。
1 | line : Example1 |
写入文件
如果你想写入文件,你需要使用作家类输出文本到一个文件中。下面的例子说明了如何可以做到这一点。
1 | import java.io.File |
如果你打开文件example.txt文件,您将看到文本中打印了“Hello World”这个词。
测试文件是否是目录
如果要查看路径是文件还是目录,可以使用File类的isFile和isDirectory选项。以下示例显示如何完成此操作。
1 | class Example { |
上面的代码将显示以下输出 -
1 | File? false |
创建目录
如果要创建一个新目录,可以使用File类的mkdir函数。以下示例显示如何完成此操作。
1 | class Example { |
如果目录E:\ Directory不存在,将创建它。
删除文件
如果要删除文件,可以使用File类的delete功能。以下示例显示如何完成此操作。
1 | class Example { |
如果存在该文件将被删除。
复制文件
Groovy还提供将内容从一个文件复制到另一个文件的功能。以下示例显示如何完成此操作。
1 | class Example { |
将创建文件Example1.txt,并将文件Example.txt的所有内容复制到此文件。
获取目录内容
以下示例显示如何使用File类的eachFile函数列出特定目录中的文件。
1 | class Example { |
输出将显示目录E:\ Temp中的所有文件
如果要递归显示目录及其子目录中的所有文件,则可以使用File类的eachFileRecurse函数。以下示例显示如何完成此操作。
1 | class Example { |
输出将显示目录E:\ Temp
中的所有文件及其子目录(如果存在)。
6. Java运行Groovy脚本
Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
可将java
代码在Groovy
脚本动态编码、代码被修改达到不重启服务的目的(类似于热部署)
核心涉及
ClassLoader
:就是类的装载器,它使JVM可以动态的载入Java类,JVM并不需要知道从什么地方(本地文件、网络等)载入Java类,这些都由ClassLoader
完成。GroovyClassLoader
:动态地加载一个脚本并执行它的行为。GroovyClassLoader是一个定制的类装载器,负责解释加载Java类中用到的Groovy类。
环境搭建
添加maven环境
1 | <!--Groovy脚本依赖--> |
创建Groovy脚本装载类
1 | public class GroovyUtils { |
定义代码
定义java接口
1 | /** |
定义Groovy实现
在resources目录下添加groovy的是实现类,并实现java的FileScanFilter接口
1 | package groovy |
Java进行调用
1 | public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException { |
测试
测试代码
运行代码后可以得到以下结果
1 | config.xml |
动态修改
我们可以在不改动java代码的情况下改动groovy代码的情况下改变业务
1 | package groovy |
再次测试
1 | 文件路径:e:\tmp\config.xml |