学习groovy基础

news/2024/10/17 22:39:01/文章来源:https://www.cnblogs.com/hellozjf/p/18473250

简介

Groovy 是一种 JVM 语言,它可以编译与 Java 相同的字节码,然后将字节码文件交给 JVM 去执行,并且可以与 Java 无缝地互操作,Groovy 可以透明地与 Java 库和代码交互,可以使用 Java 所有的库。

Groovy 也可以直接将源文件解释执行

它还极大地清理了 Java 中许多冗长的代码格式

如果你是 Java 程序员,那么学习 Groovy 简直毫无压力

Groovy 尚未成为主流的开发语言,但是它已经在测试(由于其简化的语法和元编程功能),在构建系统中占据了一席之地

既支持面向对象编程,也支持面向过程编程,既可以作为编程语言也可以作为脚本语言

环境搭建

安装 JDK

从官网下载JDK,安装好之后配置 JAVA_HOME 和 PATH

安装 Groovy

下载地址:https://groovy.apache.org/download.html,或者使用百度网盘https://pan.baidu.com/s/1OXLQGHHOrg9A6j-X0ksI7Q?pwd=1111

如果是从官网下载,需要选择版本进行下载

请注意,不同的 Groovy 版本对应的 JVM 版本也不同

Groovy-SDK 目录结构

解压apache-groovy-sdk-3.0.9.zip,解压出来的东西里面最重要的是bindoc

bin目录下面有如下的东西

doc 目录下面有如下的东西

配置 Groovy 环境变量

首先配置 GROOVY_HOME,值为刚才解压的路径

然后再配置 PATH

在控制台中输入groovy-version,校验是否正确安装

下载和安装 IDEA

创建 Groovy 工程

使用 IDEA 创建一个 Groovy 工程

新建类

运行结果如下

可以精简语法

再次精简

语法讲解

变量的类型

在 Groovy 中,没有基本数据类型,只有对象类型,表面上我们定义基本数据类型,但实际都会帮我们装箱处理

无论定义基本数据类型还是对象类型,其实都会帮我们转为对象类型

但是对于程序员来说,写代码没有影响

变量的定义

强类型定义方式

数据类型 变量名 = 初始值

弱类型定义方式

根据值可以推断出变量的数据类型,所以类型不用显示声明,直接用 def 即可

def 变量名 = 初始值

用 def 这种弱类型定义可以随便改变类型

如果不希望别人改变数据类型,用强类型

如果是你自己使用,并且想要随意更改类型,那么就用弱类型

字符串

字符串的常用定义方式

(1)单引号定义方式

(2)双引号定义方式

(3)三引号定义方式

package com.msb.test01// 单引号形式定义字符串:等价于Java中双引号的定义方式
def str1 = 'hi groo\nvy1'
println str1
println str1.class      // class java.lang.String// 双引号形式定义字符串
def str2 = "hi groovy2"
println str2
println str2.class      // class java.lang.String// 三引号形式定义字符串
def str3 = '''hi groovy3'''
println str3
println str3.class      // class java.lang.String// 三种方式区别在哪里
// 用单引号形式定义字符串,那么字符串的格式需要自己去控制,比如加:转义字符
// 用三引号的形式,我们可以直接在字符串中定义格式
// 如果想要写的形式和展示形式一样,可以在第一个三引号后面加入\
def str4 = '''\
hi 
groovy'''
println str4// 双引号形式的字符串:可扩展字符串:
def str5 = "groovy5"
def str6 = "hi ${str5}"     // 拼接变量
println str6
println str6.class      // class org.codehaus.groovy.runtime.GStringImpl// 拼接表达式:可扩展表达式融入到字符串中去
def str7 = "100 + 100 = ${100 + 100}"
println str7
println str7.class      // class org.codehaus.groovy.runtime.GStringImpl// 总结:双引号形式定义用的最多 - 因为可扩展特性// String 和 GString 使用的时候不用互相转化
// 验证:def str8 = test(str6)
println str8
println str8.class// 定义方法:
String test(String s) {return s
}

字符串的常用方法

(1)可直接使用java.lang.String中的方法

package com.msb.test01def str1 = "higroovy"
println "字符串的长度为:" + str1.length()                          // 8
println "字符串是否为空:" + str1.isEmpty()                         // false
println "获取字符串的下标对应的字符为:" + str1.charAt(2)              // gdef str2 = "higroovy"
println "判断两个字符串是否相等:" + str1.equals(str2)              // true
println "字符串从固定位置截取:" + str1.substring(3)       // roovy
println "字符串从区间位置截取:" + str1.substring(2, 4)            // gr
println "替换字符串为新字符串:" + str1.replace('o', 'O')      // higrOOvydef str3 = "a-b-c-d-e-f"
def split = str3.split("-")                 // ['a', 'b', 'c', 'd', 'e', 'f']
println "按照指定的字符串进行分裂为数组的形式:" + splitdef str4 = "higroovy"
println "转大写字母:" + str4.toUpperCase()                   // HIGROOVY
println "转小写字母:" + str4.toUpperCase().toLowerCase()     // higroovydef str5 = "  hi groovy   "
println "去掉首尾空格:" + str5.trim()         // hi groovydef str6 = "a"
def str7 = "b"
println "字符串的比较:" + str6.compareTo(str7)        // -1 

(2)使用org.codehaus.groovy.runtime.StringGroovyMethods中方法

package com.msb.test01def str1 = "higroovy"
println "以str1字符串为中心在两侧用空格进行填充为指定长度:" + str1.center(12)         // "  higroovy  "
println "以str1字符串为中心在两侧用字符a进行填充为指定长度:" + str1.center(12, 'a')       // "aahigroovyaa"println "在str1的左侧进行填充:" + str1.padLeft(10, 'a')     // "aahigroovy"def str2 = "hellogroovy"
def str3 = "groovy"
println "减法操作:去掉重复内容:" + str2.minus(str3)       // hellodef str4 = "higroovy"
println "字符串的逆转/倒序:" + str4.reverse()           // yvoorgihprintln "首字母大写:" + str4.capitalize()            // Higroovydef str5 = "123"
println "类型的转换:" + str5.toInteger().class           // class java.lang.Integer

Groovy 中新增的操作符

package com.msb.test01// 操作符 - 进行比较
def str6 = "a"
def str7 = "b"
println str6 > str7             // false// 操作符:获取字符串指定下标上的字符
def str8 = "groovy"
println str8[1]                 // r
println str8[2..3]              // oo// 操作:减法操作
def str9 = "hellogroovy"
def str10 = "groovy"
println str9 - str10            // hello

流程控制

流程控制分为:顺序结构、分支结构、循环结构

switch-case 分支

package com.msb.test04def a = "99.9"
// 在 Groovy 中 a 可以是任意类型
switch (a) {// 按照类型比较:a.class// case 后面可以按照不同类型进行判断case 'abc':println "这是第1个分支"breakcase [4, 7, 9]:     // 列表println "这是第2个分支"breakcase 45..98:        // 范围println "这是第3个分支"breakcase Integer:println "这是第4个分支"breakcase BigDecimal:println "这是第5个分支"breakdefault:println "这是第6个分支"break
}

for 循环

package com.msb.test04// 普通循环
for (def i = 1; i <= 10; i++) {println i
}
println "--------------------------"// 对范围循环
for (i in 10..30) {println i
}
println "--------------------------"// 对列表循环
for (i in [1,2,3,4,5]) {println i
}
println "--------------------------"// 对 Map 循环
for (i in [1002:"zhangsan", 2004:"lisi", 9004:"zhuliu"]) {println i.key + "---" + i.value
}

闭包

闭包的基本知识点

闭包的定义

闭包就是一段代码块,用{}括起来

def c = { println 'hi groovy' }

闭包调用/执行

c.call()
c()

闭包传入参数

无参数
// -> 前:闭包参数,-> 后:闭包体
def c = { -> println 'hi groovy' } 
c.call()
可以传入多个参数(用逗号隔开参数即可)
def c = { String str, int num -> println "hi ${str}, hi ${num}" }
def num = 19
c.call('groovy', num)
有默认的参数

所有闭包都有一个默认参数,不需要你显示声明,用it接收

def c = { println "hi ${it}" }
c.call('groovy')

如果你不想叫it,那么就需要自己手动显示将参数定义即可,一旦定义那么就没有默认参数了(隐式参数)

闭包返回值

闭包一定有返回值,如果不写,就相当于返回 null

def c = { println "hi ${it}" }
def result = c.call('groovy')
println result

可以定义返回值

def c = { return "hi ${it}" }
def result = c.call('groovy')
println result

闭包的常见使用场景

与基本数据类型结合使用(for 循环场景)

(1)案例:从 2 - 7 进行遍历:-------- upto

2.upto(7) { println it }

底层对应源码

(2)案例:1 + 2 + 3 + ... + 100 ------ upto

def result = 0
1.upto(100) { result += it }
println result

(3)案例:输出 7 ~ 2 -- downto

7.downto(2) { println it }

(4)案例:输出 100 以内的数,--- times(从 0 开始遍历到指定数结束)

3.times { println it }

结果

(5)案例:1 + 2 + 3 + ... + 100 -- times

def r = 0
101.times { r += it }
println r

补充:写法两种

// 如果闭包是调用方法的最后一个参数,可以直接将闭包写在外面
2.upto(7) { println it }		// 常用
2.upto(7, { println it })		// 不常用

与字符串结合使用

package com.msb.test05def s = "hi groovy 2023"// 遍历:如果闭包是方法的最后一个参数,我们可以写在外面,不用非要写在 () 中
println s.each { println it }       // each 的返回值就是字符串 s 本身// 找到符合条件的第一个值
println s.find { it.isNumber() }
// PS: 参照源码发现 !bcw.call(new Object[] { value })     --> 闭包返回值必须是布尔值// 找到符合条件的全部值
def list = s.findAll { it.isNumber() }
println list.toListString()// 判断任意一位是否满足条件
println s.any { it.isNumber() }// 判断每一位是否满足条件
println s.every { it.isNumber() }// 收集结果
def list2 = s.collect { it.toUpperCase() }
println list2.toListString()

闭包中的变量

  1. this,this代表定义该闭包的类的实例对象(实例闭包)或者类本身(静态闭包)
  2. owner,可以和this用法一样,还可以用作:当闭包中嵌套闭包的时候,这时候owner就指向定义它的闭包对象
  3. delegate,它的含义大多数情况下是跟owner的含义一样,除非它被显示的修改

在 Groovy 脚本中定义闭包,那么 this、owner、delegate 指代的都是当前所在的脚本的类的对象(当前脚本编译后对应的就是一个脚本类型的类)

// 定义闭包
def c1 = {println "c1-this: " + thisprintln "c1-owner: " + ownerprintln "c1-delegate: " + delegate
}// 闭包调用
c1.call()

结果

定义内部类

如果定义内部类,那么无论是闭包中还是方法中,this、owner、delegate 指代的都是所在类的对象 - Person 的对象

package com.msb.test05// 定义内部类
class Person {// 定义闭包def c2 = {println "c2-this: " + thisprintln "c2-owner: " + ownerprintln "c2-delegate: " + delegate}// 定义方法def test() {// 定义闭包def c3 = {println "c3-this: " + thisprintln "c3-owner: " + ownerprintln "c3-delegate: " + delegate}// 调用闭包c3.call()}
}// 定义 Person 对象
Person p = new Person();
// 调用内部类的闭包
p.c2.call()
// 调用内部类的方法
p.test();

如果定义的内容是静态的,那么 this,owner,delegate 指代的就是所在的类 - Person2

package com.msb.test05// 定义内部类
class Person2 {// 定义闭包def static c2 = {println "c2-this: " + thisprintln "c2-owner: " + ownerprintln "c2-delegate: " + delegate}// 定义方法def static test() {// 定义闭包def c3 = {println "c3-this: " + thisprintln "c3-owner: " + ownerprintln "c3-delegate: " + delegate}// 调用闭包c3.call()}
}// 调用内部类的闭包
Person2.c2.call()
// 调用内部类的方法
Person2.test()

结果

闭包中嵌套闭包

this 指代的依然是所在的类,但是 owner、delegate 指代的就是嵌套闭包的闭包

package com.msb.test05// 闭包中嵌套闭包
def c4 = {def c5 = {println "c5-this: " + thisprintln "c5-owner: " + ownerprintln "c5-delegate: " + delegate}c5.call()
}// 闭包的调用
c4.call()

运行结果如下:

总结1

无论什么情况下,this 指定的都是所在类/类的对象

但是如果遇到闭包嵌套闭包,owner、delegate 指代的就是嵌套闭包的闭包

owner、delegate 不同的情况:它的含义大多数情况下是跟 owner 的含义一样,除非它被显示的修改

package com.msb.test05Person p = new Person()
// 闭包中嵌套闭包
def c4 = {def c5 = {println "c5-this: " + thisprintln "c5-owner: " + ownerprintln "c5-delegate: " + delegate}c5.delegate = pc5.call()
}// 闭包的调用
c4.call()

运行结果

总结2

delegate 的含义大多数情况下是跟 owner 的含义一样,除非它被显示的修改

闭包的委托策略

PS:写脚本用的少,了解即可

package com.msb.test05// 定义 A 类
class A {String namedef ac = {"name = ${name}"}String toString() {ac.call()}
}// 定义 B 类
class B {String name
}// 定义对象
def a = new A(name: "丽丽")
def b = new B(name: "菲菲")
// 调用 a 对象的方法
println a.toString()

结果

原因:

${name}取值是从delegate中取值,所以delegate默认情况下指代的是当前 A 的对象

想要得到菲菲的结果,解决修改delegate

// 定义对象
def a = new A(name: "丽丽")
def b = new B(name: "菲菲")
// 修改 delegate
a.ac.delegate = b
// 调用 a 对象的方法
println a.toString()

但是发现修改 delegate 不好用,因为默认情况下delegate委托机制是 owner first,所以我们需要修改委托策略

// 定义对象
def a = new A(name: "丽丽")
def b = new B(name: "菲菲")
// 修改 delegate
a.ac.delegate = b
a.ac.resolveStrategy = Closure.DELEGATE_FIRST
// 调用 a 对象的方法
println a.toString()

结果

总结:${name}默认从 delegate 取值,delegate 默认和 owner 的值一样,委托机制也是 owner_first 优先,所以你光改变 delegate 的值没用,需要修改委托策略为 delegate_first

列表

列表的定义

package com.msb.test06// 定义集合
def list = new ArrayList()      // java 中的定义方式
// groovy 中的定义 ArrayList 的方式
def list2 = [1,2,3,4,5,6,7]
println list2.class             // class java.util.ArrayList
println list2.size()            // 7// groovy 中定义数组的方式
def arr = [1,2,3,4] as int[]
int[] arr2 = [1,2,3,4]
println arr.class               // class [I
println arr2.class              // class [I

列表的使用

添加元素、删除元素

package com.msb.test06// 定义列表
def list = [1,2,3,4,5,6]// 添加操作
list.add(7)                         // [1,2,3,4,5,6,7]
println list.toListString()
list.leftShift(8)                   // [1,2,3,4,5,6,7,8]
println list.toListString()
list << 9
println list.toListString()         // [1,2,3,4,5,6,7,8,9]
def newList = list + 10
println newList.toListString()      // [1,2,3,4,5,6,7,8,9,10]// 删除操作
def list2 = [1,2,3,4,5,6]
// 按照索引删除操作
list2.remove(3)                 // [1,2,3,5,6]
println list2.toListString()
list2.removeAt(0)                    // [2,3,5,6]
println list2.toListString()
// 按照指定元素删除
list2.remove((Object)5)             // [2,3,6]
println list2.toListString()
list2.removeElement(6)              // [2,3]
println list2.toListString()
// 按照指定条件删除元素
def list3 = [1,2,3,4,5,6]
list3.removeAll { it % 2 == 0 }     // 删除所有偶数
println list3.toListString()        // [1,3,5]// 使用删除符删除
def list4 = [1,2,3,4,5,6]
def newlist = list4 - [4,5,6]
println newlist                     // [1,2,3]

排序操作

package com.msb.test06// 定义列表
def list = [-3, -1, 4, 0, 5, 2, -6]// 排序操作
list.sort()         // 按照升序排序
println list        // [-6, -3, -1, 0, 2, 4, 5]// 按照指定条件排序
list.sort { num1, num2 -> Math.abs(num1) - Math.abs(num2) }     // 按照绝对值大小升序排序
println list        // [0, -1, 2, -3, 4, 5, -6]def strlist = ['a', 'abcdef', 'abc', 'ab']
strlist.sort { return it.size() }       // 按照字符串的长度排序
println strlist     // [a, ab, abc, abcdef]

查找操作

package com.msb.test06// 定义列表
def list = [-3, -1, 4, 0, 5, 2, -6]println list.find { it % 2 == 0 }       // 找到列表中的第一个偶数
println list.findAll { it % 2 == 0 }    // 找到列表中的所有偶数println list.any { it % 2 == 0 }        // 只要列表中有一个偶数,那么就返回 true
println list.every { it % 2 == 0 }      // 列表中必须每个元素都是偶数的时候,才会返回 trueprintln list.min()      // 获取最小值
println list.max()      // 获取最大值// 做统计
println list.count { return it >= 0 }

映射

映射的定义

package com.msb.test07// 定义 Java 中的 HashMap
// def hm = new HashMap()// 在 Groovy 中定义
def map = ['张三': 1001, '李四': 2003, '王五': 9006]
println map.toMapString()               // [张三:1001, 李四:2003, 王五:9006]// 添加元素:通过 key 添加 value
map['朱六'] = 9005
println map.toMapString()               // [张三:1001, 李四:2003, 王五:9006, 朱六:9005]
map.'刘七' = 7001
println map.toMapString()               // [张三:1001, 李四:2003, 王五:9006, 朱六:9005, 刘七:7001]
map.'newMap' = ['x':1,'y':2]
println map.toMapString()               // [张三:1001, 李四:2003, 王五:9006, 朱六:9005, 刘七:7001, newMap:[x:1, y:2]]// 上述代码中,key 部分是单引号的不可变字符串,可以单引号省略不写
def map2 = [张三:1001, 李四:2003, 王五:9006]
println map2.toMapString()              // [张三:1001, 李四:2003, 王五:9006]
// println map2.class       // 通过 .class 方式获取 map2 的类型不可以
println map2.getClass()                 // class java.util.LinkedHashMap
def map3 = [张三:1001, 李四:2003, 王五:9006] as Hashtable
println map3.getClass()                 // class java.util.Hashtable
Hashtable map4 = [张三:1001, 李四:2003, 王五:9006]
println map4.getClass()                 // class java.util.Hashtable

映射的使用

映射的遍历

package com.msb.test07def map = [张三:1001, 李四:2003, 王五:9006, 主六:9005, 'newMap':[x:1,y:2]]
// 进行遍历操作
map.each { println it.key + '---' + it.value }
map.each { key,value -> println key + '---' + value }// 带索引的遍历
map.eachWithIndex { Map.Entry<String, Serializable> entry, int i -> println entry.key + '---' + entry.value + ' index:' + i }
map.eachWithIndex { key, value, index -> println key + '---' + value + ' index:' + index }

映射的查找

package com.msb.test07def map = ['张三':['score':68,'sex':'女'],'李四':['score':32,'sex':'男'],'王五':['score':71,'sex':'女'],'朱六':['score':74,'sex':'男']
]
// 查找
// 找到映射中第一个 score 大于 70 的键值对
println map.find { it.value.score > 70 }
// 找到映射中所有 score 大于 70 的键值对信息
println map.findAll { it.value.score > 70 }// 找到所有分数大于 60,且性别为女的键值对数量
println map.count { it.value.score > 60 && it.value.sex == '女' }// 先查询成绩在 70 以上的键值对信息,在此基础上获取 key 的集合
println map.findAll { it.value.score > 70 }.collect { it.key}// 分组
println map.groupBy { it.value.score >= 60 ? '及格' : '不及格' }     // [及格:[张三:[score:68, sex:女], 王五:[score:71, sex:女], 朱六:[score:74, sex:男]], 不及格:[李四:[score:32, sex:男]]]

映射的排序

package com.msb.test07def map = ['张三':['score':68,'sex':'女'],'李四':['score':32,'sex':'男'],'王五':['score':71,'sex':'女'],'朱六':['score':74,'sex':'男']
]// 排序
// 按照指定的条件进行排序:按照学生的成绩排列
def newMap = map.sort { def stu1, def stu2 ->def score1 = stu1.value.scoredef score2 = stu2.value.scorereturn score1 - score2
}
println newMap

范围

package com.msb.test08// 定义一个范围
// 定义范围指的就是定义 2 到 5 的范围:2,3,4,5
def r = 2..5
println r.size()        // 4def r2 = 3..<8      // 3,4,5,6,7
println r2.size()       // 5// 操作
println r[1]            // 通过索引获取元素     // 3
println r.contains(4)   // 判断是否包含某个具体的数值        // true
println r2.from         // 范围开始的数字      // 3
println r2.to           // 范围结束的数字      // 7// 通过查看源码,发现 Range 实际就是 List 的一种,与列表的操作一致
// 有了列表为什么还要用范围?轻量级列表,如果定义连续范围可以使用范围// 遍历
r.each { println it }
for (ele in r2) {println ele
}

在 switch-case 中的应用

package com.msb.test08def a = "99.9"
// 在 Groovy 中 a 可以是任意类型
switch (a) {
// 按照类型比较:a.class
// case 后面可以按照不同类型进行判断case 'abc':println "这是第1个分支"breakcase [4, 7, 9]:     // 列表println "这是第2个分支"breakcase 45..98:        // 范围println "这是第3个分支"breakcase Integer:println "这是第4个分支"breakcase BigDecimal:println "这是第5个分支"breakdefault:println "这是第6个分支"break
}

面向对象

类的定义和对象的定义

新建 groovy 类

类的定义

package com.msb.test09// 创建对象
// 不用构造器
// def s1 = new Student()
// println s1
// 没有显示定义构造器的时候,我们依然可以在定义对象的时候对属性进行初始化赋值
def s5 = new Student(name:'丽丽',age:19)
println s5
def s6 = new Student(name:'丽丽')
println s6// 显示编写了构造器,就可以用如下的方式使用构造器
// def s2 = new Student('丽丽', 19)
// def s3 = ["娜娜", 17] as Student
// Student s4 = ["露露", 15]
// println s2
// println s3
// println s4

属性的取值

无论是用 . 的方式直接取值,还是用 get / set 的方式取值,实际底层调用的都是 get / set 方法

def s5 = new Student(name:'丽丽',age:19)
println s5// 属性的值可以自己拼接读取
println "学生的姓名是:${s5.name},学生的年龄是:${s5.age}"
println "学生的姓名是:${s5.getName()},学生的年龄是:${s5.getAge()}"

方法的定义和调用

方法的定义

// 定义方法
def m1() {    // def 相当于 Object'方法1'			// 在 groovy 中,默认方法的最后一句话为返回值,return 可以省略
}def m2(param1, param2) {		// 传入多个参数的时候用 , 隔开即可"方法2:${param1}---${param2}"
}static def m3() {'方法3'
}

方法的调用

println m1()
println m2("丽丽", "菲菲")
println T2.m3()

方法调用的补充

package com.msb.test09// 定义方法
def m1(param) {println "这是 m1 方法"
}// 对方法调用
m1("aa")
m1 "bb"         // 调用方法的时候,()可以省略不写,后面接参数列表即可,如果有多个参数,用逗号拼接参数即可
println "打印一句话"
println("打印一句话")// 定义方法
def m2(Closure c) {println "这是m2方法"
}// 调用方法
m2({String name -> println name})
// 如果闭包作为参数的话,闭包可以写在外侧
m2{String name -> println name}

接口

创建接口

package com.msb.test09interface TestInterface {void a()def b(param1)
}

PS:在 groovy 中不可以定义非 public 类型的方法

类中实现接口

package com.msb.test09// groovy 中所有东西默认都是 public 修饰的
class MyStudent implements TestInterface {// 属性String nameInteger age@Overridevoid a() {}@Overridedef b(Object param1) {return null}
}

Trait

用的少,知道即可

定义

package com.msb.test09trait Swiming {// 抽象方法:abstract 修饰符必须加上abstract void drink()// 实现方法def swim() {println "可以游泳"}
}

在 Trait 中定义抽象方法和非抽象方法,定义以后就可以让类来使用(使用和接口很像,用 implements 来实现 Trait)

package com.msb.test09class Duck implements Swiming {@Overridevoid drink() {println '游泳的时候喝到水了'}
}

在脚本中定义具体的对象调用方法

package com.msb.test09def d = new Duck()
d.drink()
d.swim()

运行结果

一个类可以实现多个 Trait(解决多继承问题)

package com.msb.test09trait Flying {def fly() {println "可以飞行"}
}
package com.msb.test09class Duck implements Swiming,Flying {@Overridevoid drink() {println '游泳的时候喝到水了'}
}
package com.msb.test09def d = new Duck()
d.drink()
d.swim()
d.fly()

结果:

PS:Trait 就像是抽象类和接口的结合,类实现用 implements 关键字实现,可以实现多个 Trait

元编程 - 方法调用和拦截

使用运行时元编程,我们可以在运行时截取类和接口的方法

定义一个类

package com.msb.test09class Person {String nameInteger age// 方法def eat() {return '可以吃饭'}
}

在脚本中创建对象,调用方法

package com.msb.test09// 定义 Person 对象
def p = new Person(name: '丽丽', age: 19)
// 调用 Person 中已有的方法直接调用
println p.eat()p.play()

发现:调用已有的eat方法,直接调用没有问题,但是调用没有的方法play会直接报错

但是在 groovy 中可以用重写方法的形式来替换不存在的方法

package com.msb.test09class Person {String nameInteger age// 方法def eat() {return '可以吃饭'}@OverrideObject invokeMethod(String name, Object args) {println '调用了invokeMethod方法'return "当前这个方法是:${name},当前这个方法的参数是:${args}"}
}
package com.msb.test09// 定义 Person 对象
def p = new Person(name: '丽丽', age: 19)
// 调用 Person 中已有的方法直接调用
println p.eat()println p.play()

结果

如果重写了methodMissing方法,会调用methodMissing方法

package com.msb.test09class Person {String nameInteger age// 方法def eat() {return '可以吃饭'}@OverrideObject invokeMethod(String name, Object args) {println '调用了invokeMethod方法'return "当前这个方法是:${name},当前这个方法的参数是:${args}"}Object methodMissing(String name, Object args) {println '调用了methodMissing方法'return "当前这个方法是:${name},当前这个方法的参数是:${args}"}
}

运行结果

元编程 - metaClass

使用运行时元编程,我们可以在运行时注入,合成类和接口的方法

package com.msb.test09// 动态为 Person 类添加 sex 属性
Person.metaClass.sex = '女'
// 创建 Person 对象
def p = new Person(name:'丽丽', age:19)
p.sex = '男'
println p.sex// 动态为 Person 类添加方法
Person.metaClass.setNameUpperCase = { -> name.toUpperCase()}
def p2 = new Person(name: "abcdef", age: 19)
println p2.setNameUpperCase()// 动态为 Person 类添加静态方法
Person.metaClass.static.setNameLowerCase = { String name -> name.toLowerCase() }
println Person.setNameLowerCase("ABCDEF")

Groovy 对 JSON 的操作

Groovy 自带的工具类处理 JSON 方式

(1)将对象转为 JSON 串

(2)将 JSON 串转为对象

package com.msb.test10import groovy.json.JsonOutput
import groovy.json.JsonSlurperdef p = new Person(name: 'Lucy', age: 19)
println JsonOutput.toJson(p)def list = [new Person(name: 'Lucy', age: 19), new Person(name: 'LiLei', age: 21), new Person(name: 'Luna', age: 32)]
println JsonOutput.toJson(list)// 打印带格式的 JSON 串
def jsonstr = JsonOutput.toJson(list)
println JsonOutput.prettyPrint(jsonstr)// 将 Json 串 -> 对象
def str = '{"age":19,"name":"lucy"}'
def js = new JsonSlurper()
def p2 = (Person) (js.parseText(str))
println p2def list2 = js.parseText('[{"age":19,"name":"lucy"},{"name":"LiLei","age":21},{"name":"Luna","age":32}]')
println list2.class

使用 Java 第三方类库处理 JSON

将第三方类库导入程序中

脚本中转换

package com.msb.test10import com.google.gson.Gson// 将对象转为 json 串
def p = new Person(name:'lili', age: 19)def gson = new Gson()
println gson.toJson(p)// 将 json 串转为对象
def p2 = gson.fromJson('{"name":"lili", "age":19}', Person.class)
println p2

Groovy 对 XML 的操作

对 XML 进行解析

package com.msb.test11final String xml = '''\
<students><student id="1"><name>张三</name><age>18</age><sex>男</sex><score>98</score></student><student id="2"><name>李四</name><age>21</age><sex>女</sex><score>93</score></student><student id="3"><name>王五</name><age>19</age><sex>女</sex><score>89</score></student>
</students>
'''// 解析 XML
def xs = new XmlSlurper()
def students = xs.parseText(xml)
// 获取节点的值
println students.student[0].name.text()// 获取属性的值
println students.student[1].@id

XML 的遍历

def list = []
students.student.each {it -> list.add(it.name.text() + '---' + it.age.text())
}println list.toListString()

生成 XML

package com.msb.test11import groovy.xml.MarkupBuilderdef s = new StringWriter()
// 生成 XML 核心类
def mb = new MarkupBuilder(s)
// 创建根节点:看上去像一个方法,但是实际上不是方法,只是语法长这样 - 伪方位
// () 中传入这个节点的属性 {} 中写入这个节点下的节点
mb.students() {// 第1个 student 节点:() 中传入 student 节点的属性,{} 中传入 student 下的节点student(id:'1') {name(a:'a', '张三')age('18')sex('男')score('98')}// 第2个 student 节点:() 中传入 student 节点的属性,{}中传入student下的节点student(id:'2') {name('李四')age() {va('21')}sex('女')score('93')}
}println s

Groovy 对文件的操作

操作普通文件

package com.msb.test12def file = new File("D:\\hellozjf\\code\\groovy\\groovy_demo\\students.xml")
file.eachLine { println it}
println '-------------------------'// 获取文件中所有内容
println file.getText()
println '-------------------------'// 返回的是一个列表,将每行内容放入列表中
def list = file.readLines()
println list.toListString()
println '-------------------------'// 读取部分内容
println file.withReader {char[] buffer = new char[100]it.read(buffer)     // 读取 100 个字符的内容return buffer
}
println '-------------------------'// 文件复制
def copy(String srcPath, String destPath) {// 确定目标文件def destFile = new File(destPath)if (!destFile.exists()) {// 如果目标文件不存在,我们创建目标文件destFile.createNewFile()}// 复制new File(srcPath).withReader {def lines = it.readLines()destFile.withWriter {lines.each {line -> it.append(line + '\r\n')}}}return true
}println copy("D:\\hellozjf\\code\\groovy\\groovy_demo\\students.xml", "D:\\hellozjf\\code\\groovy\\groovy_demo\\mystudents.xml")

将对象写入文件中

首先要定义一个可序列化的对象

class Student implements Serializable {// 属性String nameInteger age
}

将对象写入文件

def saveObject(Object obj, String path) {// 先将文件封装成对象def file = new File(path)if (!file.exists()) {file.createNewFile()}file.withObjectOutputStream {it.writeObject(obj)}return true
}def s = new Student(name:'露露',age:18)
saveObject(s, "D:\\hellozjf\\code\\groovy\\groovy_demo\\demo.txt")

从文件中读取对象

def readObject(String path) {def obj = null      // 读取的对象// 创建文件路径对应的文件对象def file = new File(path)// 判断文件不存在返回 Nullif (file == null || !file.exists()) {return null}file.withObjectInputStream {obj = it.readObject()}return obj
}// 调用方法读取
def s2 = (Student) readObject("D:\\hellozjf\\code\\groovy\\groovy_demo\\demo.txt")
println s2.name + '---' + s2.age

最终学习代码

groovy_demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/816604.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高级语言程序设计第三次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C 这个作业要求在哪里: https://edu.cnblogs.com/campus/fzu/2024C/homework/13284 学号:102400127 姓名:王子涵 4.8q2 打印引号的时候一开始忘了 然后在写d小题的时候宽度不知道怎么处理 后来询问了同学解决…

计量经济学(十)——正态性检验(Normality Test)

img { display: block; margin-left: auto; margin-right: auto } table { margin-left: auto; margin-right: auto } 正态性检验(Normality Test)是一种用于判断数据是否服从正态分布的重要统计方法,广泛应用于时间序列分析、回归分析等模型的构建与诊断中。许多统计模型,…

goldwave 循环播放

首先 得在选项 控制属性 里边,或者 右键这里选择 前奏/循环/结尾 或者 循环点 或者 循环 关于具体是什么意思,参考 https://www.goldwavechina.cn/bianji/boi-fa01.html这里有一些难以理解9.前奏/循环/结尾 这是一种特殊的播放功能,可以分三个部分播放声音。 首先,播放选择…

Response web登录操作 -2024/10/17

响应行设置响应状态码: void setStatus(int sc);设置响应头键值对: void setHeader(String name,String value);response实现重定向resp.setStatus(302);resp.setHeader("location","https://www.4399.com");前端a.html登录,将结果传给后端,用request接收…

Kail从入门到入狱第二课:mkdir、touch、vim、cat命令的基本应用

如果是日常生活,请不要使用root,因为root可以做任何事情比如"格式C盘" 创建目录:mkdir 很简单,在当前工作目录创建一个目录,如图所示测试:请说出cd /test的含义 今天我们将使用图形界面,请打开命令行创建文件:touch touch filename可以看到成功创建 文本编辑…

地平线与英伟达工具链 PTQ 工具功能参数对比与实操

1.理论简介在阅读本文之前,希望大家对 PTQ(Post-Training Quantization) 训练后量化有一定的了解~地平线 OpenExplorer 和 NVIDIA TensorRT 是两家公司为适配自己的硬件而开发的算法工具链,它们各自具有独特的特点和优势。分开看的时候,网上有很多资料,但却没找到将他们…

公网Linux环境搭建frp实现内网穿透

前提: 本实验为一台ubuntu22操作系统云主机 脚本适用于安装平台:CentOS、Debian、Ubuntu FRP项目地址:https://github.com/fatedier/frp FRP一键脚本地址:https://github.com/MvsCode/frps-onekey1、FRP服务器端一键安装脚本(脚本在本文最后有,如果在服务器上无法获取到下…

码城|计算机专业的00后转行数据分析,还有机会吗?【悟空非空也】

计算机背景学习数据分析是有优势的。数据分析需要一些技术基础,简单点的话,需要会使用 Excel ,复杂点会用 Python 进行数据挖掘和数据分析 ,当然SQL语句一定要会,自己多练习。如果再懂点深度学习,那就更加厉害啦。 有计算机底子,学习 Python 新语言应该也没有什么问题,…

网站怎么修改后台地址?如何修改网站后台登录?

修改网站通常涉及以下几个步骤,具体操作取决于您的网站类型(如静态网站或动态网站)和使用的工具或平台。以下是一些基本的指导:确定修改需求:明确您希望对网站进行哪些方面的修改,比如页面设计、功能增加、内容更新等。备份现有网站:在进行任何更改之前,确保备份当前网…

Kail入门到入狱第一课:ls、sudo、cd、以及如何关机

先进入主界面我们按下Ctrl+Alt+F1进入控制台界面。 我们输入第一个命令: sudo su这句话可以让你获得root身份,接下来输入root密码:然后输入另一个命令,可以把当前目录设置为根目录 cd /ls -al 我们开始执行今天学习的第一个命令,它可以列出当前目录所有文件的属性: ls -a…

k8s-NFS系统配置 20241017

1、NFS服务端安装-master节点 192.168.177.133# 安装nfs服务端yum install nfs-utils -y# 创建共享目录mkdir /nfs# 配置nfs共享vim /etc/exports# 添加以下一行/nfs *(rw,sync,no_root_squash) # 指明共享目录和权限设置 # 启动nfs服务,并设置开机启动systemctl start nfs-s…

[BUUCTF]丢失的MD5

[BUUCTF]丢失的MD5[BUUCTF]丢失的MD5 分析 打开,得到py文件 import hashlib for i in range(32,127):for j in range(32,127):for k in range(32,127):m=hashlib.md5()m.update(TASC+chr(i)+O3RJMV+chr(j)+WDJKX+chr(k)+ZM)des=m.hexdigest()if e9032 in des and da in des…