Groovy
hm
# 1. Groovy概述
# 1.1 Groovy概述
Apache Groovy 编程语言 (groovy-lang.org) (opens new window)
Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
Groovy是从Java衍生出来的,并且运行在Java虚拟机上的语言.其目标是不管作为脚本语言,还是编程语言,都可以简单、直接使用。Groovy 也并不会替代 Java,而是相辅相成、互补的关系,具体使用哪门语言这取决于要解决的问题和使用的场景。
# 1.2 为什么要学习Groovy
- Groovy基于JVM,这使我能够调用产品的Java代码,也能够调用Java标准库里的代码。除些之外,还可以通过Maven或Gradle使用大量的第三方Java库。
- Groovy是动态语言,扩展了Java的容器类,提供了完善的函数式编程和元编程支持。这让我们可以写出简洁而富有表现力的代码。
- Groovy提供了大量的语法糖。与Java自身繁冗的代码相比,这些语法糖大大节约了我们编写脚本的时间,减少了我的脚本的代码量。
# 2. Groovy环境
官网下载地址:https://groovy.apache.org/download.html
# 2.1 下载
# 2.2 解压
# 2.3 配置环境变量
- 在
Path
环境变量中添加 Groovy 的bin 目录路径
# 2.4 验证
启动命令窗口,输入groovy -version指令,打印如下图,即为安装成功。
# 2.5 集成IDEA
# 2.5.1 创建项目
新建Project,选择Groovy,注意要选择Project SDK和Groovy library,默认是没有Groovy library的,选择右边的 create 按钮 ,之后选择自己安装groovy的目录即可,然后点击next
# 2.5.2 新建测试:
public class Groovy_H {
public static void main(String[] args) {
System.out.println("hello groovy");
}
}
# 3. Groovy语法
# 3.1 Hello World
class Groovy_T {
// Groovy注释标记和Java一样,支持 //或者/**/
static void main(String[] args) {
// 使用 println 就可打印输出,并且类和方法默认就是public,可以不用写 (分号也可省略)
println("hello groovy")
}
- 使用 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
关键字供使用,它可以省略变量类型的定义,根据变量的值进行类型推导。
# 3.3.1 变量声明
变量声明告诉编译器为变量创建存储的位置和大小。
下面是一个变量声明的例子 -
class Example {
static void main(String[] args) {
// x is defined as a variable
String x = "Hello";
def var = "测试变量"
// The value of the variable is printed to the console
println(x);
println(var)
}
}
# 3.4 Groovy 字符串
Groovy提供了多种表示String字面量的方法。 Groovy中的字符串可以用单引号('),双引号(“)或三引号(”“”)括起来。
- 单引号''-->所见即所得,'不支持'变量内插,有'特殊字符'同样的通过'反斜杠转义'
- 双引号""-->可以通过${var}进行"变量内插(GString)" -->'常用
- 三引号"""-->可以改变输出格式
GStrings
是groovy.lang.GString的实例,并且允许文本中包含占位符,GStrings并不是String的子类,因为String类是最终类(final class)不能被继承。然而,GString与一般的字符串一样,因为Groovy能将GStrings转型为Java strings。
GString 适用于编写模板代码,因为必须动态构建字符串,上面的字符串拼接可以优化为:
@Test
void test99(){
String price = '999'
String abc2 = "价格是: ${price}";
println(abc2)
}
# 3.4 Groovy 运算符
运算符是一个符号,通知编译器执行特定的数学或逻辑操作。
大部分运算符和 Java 一样,如:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符
与 Java 不同点在于,新增了 范围运算符
Groovy支持范围的概念,并在..符号的帮助下提供范围运算符的符号。下面给出了范围运算符的一个简单示例。
def range = 0..5
这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为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
# 3.5 Groovy 循环
# 3.5.1 while循环
while语句首先通过计算条件表达式(布尔值)来执行,如果结果为真,则执行while循环中的语句。
class Example {
static void main(String[] args) {
int count = 0;
while(count<5) {
println(count);
count++;
}
}
}
通过首先计算条件表达式(布尔值)来执行 while 语句,如果结果为true,则执行while循环中的语句。从while语句中的条件的评估开始重复该过程 此循环继续,直到条件计算为false。当条件变为假时,循环终止。 然后程序逻辑继续紧跟在while语句之后的语句
# 3.5.2 for循环
for语句用于遍历一组值。
class Example {
static void main(String[] args) {
for(int i = 0;i<5;i++) {
println(i);
}
}
}
# 3.5.3 for-in循环
class Example {
static void main(String[] args) {
int[] array = [0,1,2,3];
for(int i in array) {
println(i);
}
}
}
在上面的例子中,我们首先初始化一个具有0,1,2和3的4个值的整数数组。然后我们使用for循环语句首先定义一个变量i,然后遍历数组中的所有整数 并相应地打印值。
for-in 语句也可用于循环范围。以下示例说明如何完成此操作:
class Example {
static void main(String[] args) {
for(int i in 1..5) {
println(i);
}
}
}
for-in 语句也可用于循环访问Map
class Example {
static void main(String[] args) {
def employee = ["Ken" : 21, "John" : 25, "Sally" : 22];
for(emp in employee) {
println(emp);
}
}
}
# 3.6 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
# 3.6.1 方法参数
如果一个方法的行为由一个或多个参数的值确定,则它通常是有用的。我们可以使用方法参数将值传递给被调用的方法。请注意,参数名称必须彼此不同。
使用参数的最简单的方法类型,如下所示 −
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。
然后我们从我们的 main 方法中调用 sum 方法,并将值传递给变量 a 和 b。
上述方法的输出将是值 15。
# 3.6.2 默认参数
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。
# 3.6.3 方法返回值
方法也可以将值返回到调用程序。 这在现在编程语言中是必需的,其中方法执行某种计算,然后将所需值返回到调用方法。
下面是一个带有返回值的简单方法的例子。
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。
# 3.6.4 实例方法
方法通常在 Groovy 中的类中实现,就像 Java 语言一样。类只是一个蓝图或模板,用于创建定义其属性和行为的不同对象。类对象显示由其类定义的属性和行为。因此,通过在类中创建方法来定义行为。
以下是如何实现方法的示例。
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。
# 3.6.5 本地和外部参数名称
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
# 3.7 Groovy 列表
# 3.7.1 集合定义
定义一个列表集合的方式有点像 Java 中的定义数组一样,默认的类型就是 ArrayList
//1. 在 Groovy 中定义的集合默认就是对应于 Java 中 ArrayList 集合
def list = [1,2,3,4,5]
println list
//2. 在集合中可以介绍任意类型的数据,例如当前传入的是数字,字符串,boolean值
list = ['张三',true,5]
println list
# 3.7.2 获取指定角标下的元素
可以通过角标访问集合指定位置的元素,正数角标是从0位置左往右算起,负数角标是从0位置往反方向算。
下面的代码片段中出现的负数角标,就有别于 JAVA ,因为在 JAVA 中出现负数角标,基本就会报异常了,0 就是第一个位置的元素,-1就是最后一个位置的元素,一次类推即可
def list = [1,2,3,4,5]
//从头开始获取到第二个元素
println list[1]
//从末尾开始获取倒数第一个元素
println list[-1]
# 3.7.3 获取指定范围的元素
list[index1..index2]取出指定范围的元素
def list = [1,2,3,4,5]
//打印第二个元素到第四个元素
println list[2..4]
# 3.7.4 添加元素
在列表集合中添加元素的方式有以下三种
def list = [1, 2, 3, 4, 5]
//add 添加元素
list.add 6
//leftShift 添加元素
list.leftShift 7
//<< 添加元素
list << 8
print list
# 3.7.5 移除元素
def list = [1, 2, 3, 4, 5]
//通过下标移除元素
list.remove 2
//移除元素为2的对象
list.remove((Object)2)
//删除最后一个元素
list.removeLast()
println list
# 3.7.6 元素遍历
在
Groovy
中使用each
来遍历集合,在遍历时,可以选择是否带有角标来选择不同的遍历方法
def list = [1, 2, 3, 4, 5]
//不带有角标的遍历,类似于 java 中的 foreach
list.each { println "元素的结果是${it}" }
//带有角标的遍历,类似于普通的for循环
list.eachWithIndex { int value, int index ->
println "value is ${value} and index is ${index}"
}
# 3.8 Groovy 映射
映射(也称为关联数组,字典,表和散列)是对象引用的无序集合。Map集合中的元素由键值访问。 Map中使用的键可以是任何类。当我们插入到Map集合中时,需要两个值:键和值。
# 3.8.1 Map定义
Map 集合的定义有别于 Java 的定义方式,格式如下
def map = [key1:value1,key2:value2,...]
Groovy 中定义的 Map 默认类型是 java.util.LinkedHashMap
def map = [name: "张三", age: 18]
println map.getClass()
# 3.8.2 获取元素值
Map 集合中指定 key 下的值有有两种方式
def map = [name: "张三", age: 25]
//通过java的方式获取value
println map.get("name")
//通过中括号方式获取value
println map["name"]
//通过"."的方式获取value
println map.name
//还可以通过GString的方式进行访问
println "姓名是:${map['name']},年龄是:${map.age}"
# 3.8.3 添加元素
有两种方式可以添加map的值
def map = [name: "张三", age: 25]
//通过中括号进行设置值
map['sex'] = "男"
//通过”.“的方式来设置值
map.level = 10
println map
# 3.8.4 Map 遍历
跟
List
集合一样,Map
集合的遍历也是使用each
方法来实现。
def map = [name: "张三", age: 25]
//不带角标的遍历
map.each { println "key:${it.key},value:${it.value}" }
//带角标的方式遍历
map.eachWithIndex { Map.Entry entry, int i ->
{
println entry.key + "-" + entry.value + " index = " + i
}
}
# 3.9 Groovy 面向对象
在Groovy中,如在任何其他面向对象语言中一样,存在类和对象的概念以表示编程语言的对象定向性质。Groovy类是数据的集合和对该数据进行操作的方法。在一起,类的数据和方法用于表示问题域中的一些现实世界对象。
Groovy中的类声明了该类定义的对象的状态(数据)和行为。因此,Groovy类描述了该类的实例字段和方法。
以下是Groovy中的一个类的示例。类的名称是Student,它有两个字段 - StudentID和StudentName。在main函数中,我们创建一个这个类的对象,并将值分配给对象的StudentID和StudentName。
class Student {
int StudentID;
String StudentName;
static void main(String[] args) {
Student st = new Student();
st.StudentID = 1;
st.StudentName = "Joe"
}
}
# 3.9.1 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的值,例如method - void setStudentName(String pName)
# 3.9.2 实例方法
在类中包含更多的方法通常是一个很自然的事情,它实际上为类实现了一些功能。在我们的学生示例中,让我们添加Marks1,Marks2和Marks3的实例成员,以表示学生在3个科目中的标记。然后我们将添加一个新的实例方法,计算学生的总分。以下是代码的外观。
在下面的示例中,Total方法是一个额外的Instance方法,它内置了一些逻辑。
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.9.3 继承
继承可以定义为一个类获取另一个类的属性(方法和字段)的过程。通过使用继承,信息以分级顺序可管理。
继承其他属性的类称为子类(派生类,子类),属性继承的类称为超类(基类,父类)。
extends是用于继承类的属性的关键字。下面给出了extends关键字的语法。在下面的例子中,我们做了以下事情 -
- 创建一个名为Person的类。这个类有一个名为name的实例成员。
- 创建一个名为Student的类,它从Person类继承。请注意,在Person类中定义的名称实例成员在Student类中继承。
- 在Student类构造函数中,我们调用了基类构造函数。
- 在我们的Student类中,我们添加了2个StudentID和Marks1的实例成员。
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
# 3.9.4 内部类
内部类在另一个类中定义。封闭类可以像往常一样使用内部类。另一方面,内部类可以访问其封闭类的成员,即使它们是私有的。不允许除封闭类之外的类访问内部类。
下面是一个外部和内部类的例子。在下面的例子中,我们做了以下事情 -
- 创建一个名为Outer的类,它将是我们的外部类。
- 在Outer类中定义名为name的字符串。
- 在我们的外类中创建一个内部或嵌套类。
- 请注意,在内部类中,我们可以访问在Outer类中定义的名称实例成员。
class Example {
static void main(String[] args) {
Outer outobj = new Outer();
outobj.name = "tom";
outobj.callInnerMethod()
}
}
class Outer {
String name;
def callInnerMethod() {
new Inner().methodA()
}
class Inner {
def methodA() {
println(name);
}
}
}
当我们运行上面的程序,我们将得到以下结果 -
tom
# 3.9.5 抽象类
抽象类表示通用概念,因此,它们不能被实例化,被创建为子类化。他们的成员包括字段/属性和抽象或具体方法。抽象方法没有实现,必须通过具体子类来实现。抽象类必须用抽象关键字声明。抽象方法也必须用抽象关键字声明。
在下面的示例中,请注意,Person类现在是一个抽象类,不能被实例化。还要注意,在抽象类中有一个名为DisplayMarks的抽象方法,没有实现细节。在学生类中,必须添加实现细节。
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
# 3.9.6 接口
接口定义了类需要遵守的契约。接口仅定义需要实现的方法的列表,但是不定义方法实现。需要使用interface关键字声明接口。接口仅定义方法签名。接口的方法总是公开的。在接口中使用受保护或私有方法是一个错误。
以下是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
# 4. 闭包
闭包是Groovy语言的精髓之一,Groovy的闭包大大简化了容器的遍历,提升了代码的可扩展性,使代码更加简洁优雅,闭包在Groovy编程中几乎无处不在。
# 4.1 什么是闭包
闭包(Closure)是很多编程语言中很重要的概念,那么Groovy中闭包是什么,官方定义是“Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量”,简而言之,他说一个匿名的代码块,可以接受参数,有返回值,那么到底是怎么样的
# 4.1.1 闭包特点
groovy 中的闭包是一个开放的匿名代码块,可以接受参数,返回值可以赋值给变量。
我们通常学习的 lamb 表达式是有一定封闭空间,无法访问闭包的变量
int anser = 42;
Supplier supplier = () -> anser;
//这一行注释解开那将会报错,不能进行编译
//anser = 45;
System.out.println(supplier.get());
而在 groovy 的闭包是可以访问到外部变量的,虽然这样更自由但是也打破这个封闭空间
int anser = 42;
def supplier = { anser }
anser = 45;
println supplier.call()
# 4.2 如何定义
定义闭的语意 :
{ [closureParameters -> ] statements }
,其中[closureParameters->]代表参数们,多参数用逗号分割,用->
隔开参数与内容,没有参数可以不写->
,其中[]
内是可选的闭包参数,可省略。当闭包带有参数,就需要->
来将参数和闭包体相分离
# 4.2.1 直接定义
直接用大括号定义就是一个最简单的闭包
def hello = {println "hello world"}
hello()
# 4.2.2 传递参数
闭包是可以传递参数的,类似于
lamda
表达式的使用
def hello = {x -> println "参数值:${x}"}
hello(100)
# 4.2.3 接受不同类型参数
def info = { String name,int age -> name + "," + age}
println info("matthew",30)
# 4.2.4 使用隐含变量 it
有时候我们看到闭包没有传递参数但是可以使用参数
it
,这是使用到了闭包的隐含参数
{it -> println "she is ${it}" }
//上面的闭包可以利用隐含参数简化为
{println "she is ${it}"}
# 4.2.5 作为一个对象使用
闭包在groovy中是
groovy.lang.Closure
类的实例,这使得闭包可以赋值给变量或字段。
def closure = {println "hello world"}
assert closure instanceof Closure
Closure callback = {println "方法执行成功"}
Closure<Boolean> isOK = {boolean flag-> if(flag) return true}
# 4.3 闭包的调用
# 4.3.1 直接调用
上面我们已经了解到了如何调用闭包,最简单的方式就是直接调用闭包加上一个括号就可以调用了,括号里面可以传递闭包参数
def hello = {println "hello world"}
hello();
# 4.3.2 参数传递
闭包是可以传递参数的,如果有参数是可以通过括号进行传递的,如果多个参数可以在后面加上逗号
def hello = {x -> println "参数值:${x}"}
hello(100)
def info = { String name,int age -> name + "," + age}
println info("matthew",30)
# 4.3.3 闭包返回结果
因为闭包是一个函数,所以可以直接调用就可以获取返回结果
def sqrt = { x -> return x * x }
def result = sqrt(10)
println result
//也可以直接输出
def sqrt = { x -> return x * x }
println sqrt(10)
# 4.3.4 通过call调用
上面都是通过直接函数调用来进行调用,还可以通过
call
来进行调用
def sqrt = { x -> return x * x }
println sqrt.call(10)
# 4.3.5 闭包的参数
- 参数是可以有类型也可以不定义类型
- 有一个隐含参数 it
- 接受可变参数
def greeting = { String ...name -> "hey ${name.join(" ")}" }
println greeting("matthew","jerry")
# 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代码的基本写法
FileInputStream fin = null;
try {
fin = new FileInputStream("E:/tmp/xxx.log");
BufferedReader br = new BufferedReader(new InputStreamReader(fin));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
// TODO: handle exception
} catch (IOException e) {
// TODO: handle exception
} finally {
try {
if (fin != null) {
fin.close();
}
}
catch (IOException e2) {
// TODO: handle exception
}
}
对一个文件进行读取的时候,基本上都会用到上面的代码,重复的写这些代码让人感觉很枯燥,同时在阅读这样的代码的时候也极易干扰视线。真正要干的事情也就是把文件内容输出而已。
# 5.1 读取文件
以下示例将输出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
# 5.2 读取文件的内容到字符串
如果要将文件的整个内容作为字符串获取,可以使用文件类的text属性。以下示例显示如何完成此操作。
class Example {
static void main(String[] args) {
File file = new File("E:/Example.txt")
println file.text
}
}
如果该文件包含以下行,它们将被打印出来。
line : Example1
line : Example2
# 5.3 写入文件
如果你想写入文件,你需要使用作家类输出文本到一个文件中。下面的例子说明了如何可以做到这一点。
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”这个词。
# 5.4 测试文件是否是目录
如果要查看路径是文件还是目录,可以使用File类的isFile和isDirectory选项。以下示例显示如何完成此操作。
class Example {
static void main(String[] args) {
def file = new File('E:/')
println "File? ${file.isFile()}"
println "Directory? ${file.isDirectory()}"
}
}
上面的代码将显示以下输出 -
File? false
Directory? True
# 5.5 创建目录
如果要创建一个新目录,可以使用File类的mkdir函数。以下示例显示如何完成此操作。
class Example {
static void main(String[] args) {
def file = new File('E:/Directory')
file.mkdir()
}
}
如果目录E:\ Directory不存在,将创建它。
# 5.6 删除文件
如果要删除文件,可以使用File类的delete功能。以下示例显示如何完成此操作。
class Example {
static void main(String[] args) {
def file = new File('E:/Example.txt')
file.delete()
}
}
如果存在该文件将被删除。
# 5.7 复制文件
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的所有内容复制到此文件。
# 5.8 获取目录内容
以下示例显示如何使用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中的所有文件及其子目录(如果存在)。
# 6. Java运行Groovy脚本
Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
可将java
代码在Groovy
脚本动态编码、代码被修改达到不重启服务的目的(类似于热部署)
# 6.1 核心涉及
ClassLoader
:就是类的装载器,它使JVM可以动态的载入Java类,JVM并不需要知道从什么地方(本地文件、网络等)载入Java类,这些都由ClassLoader
完成。GroovyClassLoader
:动态地加载一个脚本并执行它的行为。GroovyClassLoader是一个定制的类装载器,负责解释加载Java类中用到的Groovy类。
# 6.2 环境搭建
# 6.2.1 添加maven环境
<!--Groovy脚本依赖-->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.5.14</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
# 6.2.2 创建Groovy脚本装载类
public class GroovyUtils {
private final static ClassLoader classLoader = GroovyUtils.class.getClassLoader();//获取当前类装载器
//ClassLoader:就是类的装载器,它使JVM可以动态的载入Java类,JVM并不需要知道从什么地方(本地文件、网络等)载入Java类,这些都由ClassLoader完成。
public final static GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
//GroovyClassLoader:负责在运行时编译groovy源代码为Class的工作,从而使Groovy实现了将groovy源代码动态加载为Class的功能。
/**
* .
* 获取实例化对象
*
* @param script groovy脚本内容
* @param <T>
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static <T> T instanceTaskGroovyScript(String script) throws IllegalAccessException, InstantiationException {
Class taskClz = groovyClassLoader.parseClass(script);
T instance = (T) taskClz.newInstance();
return instance;
}
}
# 6.3 定义代码
# 6.3.1 定义java接口
/**
* 文件扫描接口
*/
public interface FileScanFilter {
public List<String> scan(String path);
}
# 6.3.2 定义Groovy实现
在resources目录下添加groovy的是实现类,并实现java的FileScanFilter接口
package groovy
import com.heima.buzz.FileScanFilter
/**
* Groovy目录扫描是是实现
*/
class FileScanFilterImpl implements FileScanFilter {
@Override
List<String> scan(String path) {
File file = new File(path)
List<String> fileList = new ArrayList<>();
file.eachFileRecurse {
fileList << "${it.name}".toString()
}
return fileList
}
}
# 6.3.3 Java进行调用
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException {
//加载groovy文件
String script = IOUtils.toString(GroovyUtils.class.getResourceAsStream("/groovy/FileScanFilterImpl.groovy"), "UTF-8");
//读取文件并使用groovy的类加载器加载groovy文件
FileScanFilter filter = GroovyUtils.instanceTaskGroovyScript(script);
//扫描路径下的文件
List<String> list = filter.scan("e:/tmp");
//并将结果返回
list.forEach(str-> System.out.println(str));
}
# 6.4 测试
# 6.4.1 测试代码
运行代码后可以得到以下结果
config.xml
data.json
dsl.txt
out.txt
xxx.log
xxx.txt
yiming_oa_back.zip
# 6.5 动态修改
我们可以在不改动java代码的情况下改动groovy代码的情况下改变业务
package groovy
import com.heima.buzz.FileScanFilter
import groovy.io.FileType
/**
* Groovy目录扫描是是实现
*/
class FileScanFilterImpl implements FileScanFilter {
@Override
List<String> scan(String path) {
File file = new File(path)
List<String> fileList = new ArrayList<>();
file.eachFileRecurse(FileType.FILES) {
fileList << "文件路径:${it.path}".toString()
}
return fileList
}
}
# 6.5.1 再次测试
文件路径:e:\tmp\config.xml
文件路径:e:\tmp\data.json
文件路径:e:\tmp\dsl.txt
文件路径:e:\tmp\out.txt
文件路径:e:\tmp\xxx.log
文件路径:e:\tmp\xxx.txt