简介
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
,解压出来的东西里面最重要的是bin
和doc
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.test01
def str1 = "higroovy"
println "字符串的长度为:" + str1.length() // 8
println "字符串是否为空:" + str1.isEmpty() // false
println "获取字符串的下标对应的字符为:" + str1.charAt(2) // g
def str2 = "higroovy"
println "判断两个字符串是否相等:" + str1.equals(str2) // true
println "字符串从固定位置截取:" + str1.substring(3) // roovy
println "字符串从区间位置截取:" + str1.substring(2, 4) // gr
println "替换字符串为新字符串:" + str1.replace('o', 'O') // higrOOvy
def str3 = "a-b-c-d-e-f"
def split = str3.split("-") // ['a', 'b', 'c', 'd', 'e', 'f']
println "按照指定的字符串进行分裂为数组的形式:" + split
def str4 = "higroovy"
println "转大写字母:" + str4.toUpperCase() // HIGROOVY
println "转小写字母:" + str4.toUpperCase().toLowerCase() // higroovy
def str5 = " hi groovy "
println "去掉首尾空格:" + str5.trim() // hi groovy
def str6 = "a"
def str7 = "b"
println "字符串的比较:" + str6.compareTo(str7) // -1
(2)使用org.codehaus.groovy.runtime.StringGroovyMethods
中方法
package com.msb.test01
def 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) // hello
def str4 = "higroovy"
println "字符串的逆转/倒序:" + str4.reverse() // yvoorgih
println "首字母大写:" + str4.capitalize() // Higroovy
def 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.test04
def a = "99.9"
// 在 Groovy 中 a 可以是任意类型
switch (a) {
// 按照类型比较:a.class
// case 后面可以按照不同类型进行判断
case 'abc':
println "这是第1个分支"
break
case [4, 7, 9]: // 列表
println "这是第2个分支"
break
case 45..98: // 范围
println "这是第3个分支"
break
case Integer:
println "这是第4个分支"
break
case BigDecimal:
println "这是第5个分支"
break
default:
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.test05
def 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()
闭包中的变量
- this,this代表定义该闭包的类的实例对象(实例闭包)或者类本身(静态闭包)
- owner,可以和this用法一样,还可以用作:当闭包中嵌套闭包的时候,这时候owner就指向定义它的闭包对象
- delegate,它的含义大多数情况下是跟owner的含义一样,除非它被显示的修改
在 Groovy 脚本中定义闭包,那么 this、owner、delegate 指代的都是当前所在的脚本的类的对象(当前脚本编译后对应的就是一个脚本类型的类)
// 定义闭包
def c1 = {
println "c1-this: " + this
println "c1-owner: " + owner
println "c1-delegate: " + delegate
}
// 闭包调用
c1.call()
结果
定义内部类
如果定义内部类,那么无论是闭包中还是方法中,this、owner、delegate 指代的都是所在类的对象 - Person 的对象
package com.msb.test05
// 定义内部类
class Person {
// 定义闭包
def c2 = {
println "c2-this: " + this
println "c2-owner: " + owner
println "c2-delegate: " + delegate
}
// 定义方法
def test() {
// 定义闭包
def c3 = {
println "c3-this: " + this
println "c3-owner: " + owner
println "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: " + this
println "c2-owner: " + owner
println "c2-delegate: " + delegate
}
// 定义方法
def static test() {
// 定义闭包
def c3 = {
println "c3-this: " + this
println "c3-owner: " + owner
println "c3-delegate: " + delegate
}
// 调用闭包
c3.call()
}
}
// 调用内部类的闭包
Person2.c2.call()
// 调用内部类的方法
Person2.test()
结果
闭包中嵌套闭包
this 指代的依然是所在的类,但是 owner、delegate 指代的就是嵌套闭包的闭包
package com.msb.test05
// 闭包中嵌套闭包
def c4 = {
def c5 = {
println "c5-this: " + this
println "c5-owner: " + owner
println "c5-delegate: " + delegate
}
c5.call()
}
// 闭包的调用
c4.call()
运行结果如下:
总结1
无论什么情况下,this 指定的都是所在类/类的对象
但是如果遇到闭包嵌套闭包,owner、delegate 指代的就是嵌套闭包的闭包
owner、delegate 不同的情况:它的含义大多数情况下是跟 owner 的含义一样,除非它被显示的修改
package com.msb.test05
Person p = new Person()
// 闭包中嵌套闭包
def c4 = {
def c5 = {
println "c5-this: " + this
println "c5-owner: " + owner
println "c5-delegate: " + delegate
}
c5.delegate = p
c5.call()
}
// 闭包的调用
c4.call()
运行结果
总结2
delegate 的含义大多数情况下是跟 owner 的含义一样,除非它被显示的修改
闭包的委托策略
PS:写脚本用的少,了解即可
package com.msb.test05
// 定义 A 类
class A {
String name
def 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 } // 列表中必须每个元素都是偶数的时候,才会返回 true
println 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.test07
def 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.test07
def 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.test07
def 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.score
def score2 = stu2.value.score
return score1 - score2
}
println newMap
范围
package com.msb.test08
// 定义一个范围
// 定义范围指的就是定义 2 到 5 的范围:2,3,4,5
def r = 2..5
println r.size() // 4
def 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.test08
def a = "99.9"
// 在 Groovy 中 a 可以是任意类型
switch (a) {
// 按照类型比较:a.class
// case 后面可以按照不同类型进行判断
case 'abc':
println "这是第1个分支"
break
case [4, 7, 9]: // 列表
println "这是第2个分支"
break
case 45..98: // 范围
println "这是第3个分支"
break
case Integer:
println "这是第4个分支"
break
case BigDecimal:
println "这是第5个分支"
break
default:
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.test09
interface TestInterface {
void a()
def b(param1)
}
PS:在 groovy 中不可以定义非 public 类型的方法
类中实现接口
package com.msb.test09
// groovy 中所有东西默认都是 public 修饰的
class MyStudent implements TestInterface {
// 属性
String name
Integer age
@Override
void a() {
}
@Override
def b(Object param1) {
return null
}
}
Trait
用的少,知道即可
定义
package com.msb.test09
trait Swiming {
// 抽象方法:abstract 修饰符必须加上
abstract void drink()
// 实现方法
def swim() {
println "可以游泳"
}
}
在 Trait 中定义抽象方法和非抽象方法,定义以后就可以让类来使用(使用和接口很像,用 implements 来实现 Trait)
package com.msb.test09
class Duck implements Swiming {
@Override
void drink() {
println '游泳的时候喝到水了'
}
}
在脚本中定义具体的对象调用方法
package com.msb.test09
def d = new Duck()
d.drink()
d.swim()
运行结果
一个类可以实现多个 Trait(解决多继承问题)
package com.msb.test09
trait Flying {
def fly() {
println "可以飞行"
}
}
package com.msb.test09
class Duck implements Swiming,Flying {
@Override
void drink() {
println '游泳的时候喝到水了'
}
}
package com.msb.test09
def d = new Duck()
d.drink()
d.swim()
d.fly()
结果:
PS:Trait 就像是抽象类和接口的结合,类实现用 implements 关键字实现,可以实现多个 Trait
元编程 - 方法调用和拦截
使用运行时元编程,我们可以在运行时截取类和接口的方法
定义一个类
package com.msb.test09
class Person {
String name
Integer 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.test09
class Person {
String name
Integer age
// 方法
def eat() {
return '可以吃饭'
}
@Override
Object 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.test09
class Person {
String name
Integer age
// 方法
def eat() {
return '可以吃饭'
}
@Override
Object 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.test10
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
def 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 p2
def list2 = js.parseText('[{"age":19,"name":"lucy"},{"name":"LiLei","age":21},{"name":"Luna","age":32}]')
println list2.class
使用 Java 第三方类库处理 JSON
将第三方类库导入程序中
脚本中转换
package com.msb.test10
import 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.test11
final 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.test11
import groovy.xml.MarkupBuilder
def 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.test12
def 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 name
Integer 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)
// 判断文件不存在返回 Null
if (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