首页 > 其他分享 >学习groovy基础

学习groovy基础

时间:2024-10-17 22:32:38浏览次数:6  
标签:groovy 闭包 name 基础 学习 println com msb def

简介

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.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()

闭包中的变量

  1. this,this代表定义该闭包的类的实例对象(实例闭包)或者类本身(静态闭包)
  2. owner,可以和this用法一样,还可以用作:当闭包中嵌套闭包的时候,这时候owner就指向定义它的闭包对象
  3. 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

最终学习代码

groovy_demo

标签:groovy,闭包,name,基础,学习,println,com,msb,def
From: https://www.cnblogs.com/hellozjf/p/18473250

相关文章

  • C/C++语言基础--C++四大类型转换讲解
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言通过前面几节课,我们学习了抽象、封装、继承、多态、异常等概念,这一篇我们将继续学习C++的类型转换,和C语言还有很大区别的;在本节课最后,也简要说了一下在计算机视角上看类型是什么样子的;C语言后面也会继续更新知识点,......
  • 2024-2025-1 20241401 《计算机基础与程序设计》 第四周学习总结
    班级链接2024计算机基础与程序设计作业要求作业要求作业目标①门电路②组合电路,逻辑电路③冯诺依曼结构④CPU,内存,IO管理⑤嵌入式系统,并行结构⑥物理安全教材学习内容总结《计算机科学概论》第四章、第五章门:非(NOT)门、与(AND)门、或(OR)门、异或(XOR)......
  • Java常用类和基础API
    目录一、String1.1String的理解1.1.1类的声明1.1.2内部声明的属性:1.2String的实例与连接1.2.1字符串常量的存储位置1.2.2 Strinq的不可变性的理解1.2.3String的连接操作:+1.3String的构造器与常用方法1.3.1常用方法1.4String的课后练习题目1题目2题目......
  • 吴恩达深度学习笔记(4)---加速神经网络训练速度的优化算法
    机器学习的应用是一个高度依赖经验,不断重复的过程,需要训练很多模型才能找到一个确实好用的。小批量梯度下降算法:矢量化可以有效计算m个算例而不需要for循环,因此我们需要将所有的训练样例放入巨型矩阵中。但是当数据量超大时,计算时间仍需很久,可以考虑将训练集分为微小的训练集......
  • Winform控件基础与进阶----DataGridView
    Winform控件基础之封装一个通用的DataGridView操作类1、创建Winform项目2、创建公共控件操作文件夹3、主界面1、控件布局2、提取通用方法3、静态方法类实现4、其他工具类实现1、JsonHelper工具类实现2、TxtOperateHelper工具类实现5、数据模型实现1、创建表结构模型2......
  • 计算机网络基础(2)---网络传输基本流程与Socket编程预备
    个人主页:C++忠实粉丝欢迎点赞......
  • 【视频讲解】共享单车使用量预测:RNN, LSTM,GRU循环神经网络和传统机器学习
    全文链接:https://tecdat.cn/?p=37899原文出处:拓端数据部落公众号分析师:XuyanReng 随着城市化进程的加速,共享单车作为一种绿色、便捷的出行方式,在城市交通中扮演着日益重要的角色。准确预测共享单车的使用量对于优化资源配置、提高运营效率以及满足用户需求具有关键意义。一......
  • 应用程序框架基础
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • 深度学习入门知识点小结
    深度学习(DeepLearning)      简介:             机器学习的分支,是一种以神经网络为架构,对数据进行特征学习是算法      深度学习(DL)与机器学习(ML)的区别:             1.特征提取                  ......
  • Makefile入门学习过程中的一些知识点-一些常见规则或语法:
    1.order-only依赖:还是以上一篇的sudoku项目为例,之前写的目标之后的依赖都属于普通依赖,普通依赖都对应自身的规则,order-only依赖也是一样的,但是当依赖文件中的内容发生改动的时候,两种依赖就会产生差别:对于普通依赖而言,当依赖发生改变需要重新与目标文件生成链接,也就是说如果任......