Groovy学习笔记

Start

基础语法

groovy是一种用于java虚拟机的一种动态语言,与java语言一样,被编译为.class文件后在jvm上执行。不同的是,groovy既可以以类的方式执行,也可以以脚本的方式执行。与大多数动态语言类似,groovy可以省略结束符;

// 类的形式 
class HelloGroovy {
    static void main(args) {
        println('hello groovy !!!')
    }
}
//直接写脚本
println('hello groovy !!!')

groovy定义变量继承了java的数据类型,即可以使用(int、double、String…)定义变量,但定义的变量都是引用类型(groovy中没有基本数据类型)。

int x = 10
println x
println x.class

作为动态语言的一种,groovy当然支持类型推断。使用def定义变量,编译器会自动推断数据类型。

def x = 10
println x.class

使用动态类型的好处是可以为变量赋其他类型的值,可以使程序更加灵活。

def x = 10
println x.class

x = 22.36
println x.class

字符串

groovy字符串继承了java中String类所有功能的同时增加了更多的方法和操作符,字符串的功能变得更加灵活。

字符串可以使用单引号,双引号,和三引号定义。

// 单引号
def name = 'Lucy'
// 三引号定义的字符串可以固定格式
def emails = '''\
Lucy@139.com
Alice@google.com
Tom@163.com
'''
/**
** 双引号定义的变量中可以填充groovy表达式
** 在这种情况下字符串类型为GString而非String
**/
def code = "19fv32"
println code.class

def result = "code : ${code}"
println result.class

groovy对字符串扩展了各种操作符

def strA = 'hello groovy'
def strB = 'hello'

// 根据索引取得字符
println strA[0]
// 根据索引范围截取字符
println strA[0..4]
// 根据编码比较字符
println strA > strB
// 字符串减法
println strA - strB

在java.lang.String类的基础上,groovy为字符串增加了许多操作方法。

def str = 'groovy'

// 居中填充
println str.center(8,'a')
// 左侧填充
println str.padLeft(8,'a')
// 右侧填充
println str.padRight(8,'a')
// 首字母大写
println strA.capitalize()
// 转换为其他类型
println '123'.toInteger().class

逻辑控制

groovy中的if_else与java中的用法相同,但是switch_case与java中的用法稍有差别,switch可以进行任意类型的匹配。

def x = 12,result

switch(x) {
    case 'string':
        result = 'string'
        break
    case [4,5,6,'7']:  // 列表
        result = 'list'
        break
    case 2..9:  // 范围
        result = 'list'
        break
    case Integer:
        result = 'integer'
        break
    case BigDecimal:
        result = 'Big Decimal'
        break
    default:
        result = 'undefined'
}

println result

for循环增量加了更加灵活的方式,可以对范围循环,可以使用for-in语法。

// 对范围进行循环
def sum = 0
for (x in 0..9) {
    sum += x
}
// 对list进行循环
for (i in [0, 1, 2, 3, 4, 5, 6]) {
    sum += i
}
// 对map进行循环
for (entity in ['first':1,'second':2]) {
    sum += entity.value
}

闭包基础

groovy中的闭包是一段大括号包裹的代码块,可以传递参数(与java中的方法相似)。

// 闭包的定义与调用
def closer = {
    println 'hello groovy !!!'
}
closer.call()
closer()

闭包传递参数类似与lamda表达式的形式,在箭头前面的是传递的参数,如果有多个参数,使用逗号进行分割。

def closer = {String name,int age -> println "hello ${name} , my age is ${age}"}
closer('groovy',10)

闭包存在一个默认参数(隐式参数),如果只用到了一个参数,可以省略参数的定义和箭头(默认参数写做it),在使用时直接传入即可。

def closer = {println "hello ${it}"}
closer('groovy');

闭包一定会参在返回值。

def closer = {String name,int age -> return "hello ${name} , my age is ${age}"}
println closer('groovy',10)

如果在闭包中不适用return关键字进行返回,则默认返回为null

def closer = {String name,int age -> println "hello ${name} , my age is ${age}"}
println closer('groovy',10)

闭包结合方法求指定数字的阶乘。

int fab(int num) {
    int result = 1;
    /**
     * 从1增长至num,每增长一次,调用一次闭包
     */
    1.upto(num, {number -> result *= number})
    // num.downto(1, {number -> result *= number})
    return result;
}

println fab(5)

闭包也可以将大括号写在方法外并省略小括号,例:从零累加至某一值。

int sum(int num) {
    int result = 0;
    /**
     * times方法会从0增长至num
     */
    num.times{ //闭包写在方法外
        number -> result += number
    }
    return result;
}

println sum(101)

闭包也可以结合字符串使用。

def str = 'the 2 and 3 is 5'

// 遍历字符串
str.each {temp -> print temp}

// 查找符合条件的第一个字符
println str.find{temp -> temp.isNumber()}

// 查找符合条件的所有字符
println str.findAll{temp -> temp.isNumber()}

// 判断字符串中是否有字符满足条件
println str.any{temp -> temp.isNumber()}

// 判断字符串中是否全部字符都满足条件
println str.every{temp -> temp.isNumber()}

// 将字符串中每一个字符转换后返回
println str.collect{it.toUpperCase()}

闭包进阶

闭包有三个重要变量:this、owner、delegate。this代表闭包定义处的类,owner代表闭包定义处的类或对象(闭包可以定义在其他闭包中,这样owner表示的就不是当前类了,与this对象就不一样了),delegate代表任意第三方对象,其默认值为owner所指向的对象。因此,在大多数情况下this、owner、delegate指向同一对象。

def closure = {
    println this == owner
    println this == delegate
}
closure.call()

在闭包中定义闭包,此时this与owner就会指向不同的对象(this指向当前类,owner和delegate只想外部闭包outClosure的实例对象)

def outClosure = {
    def innerClosure = {
        println 'this : ' + this
        println 'owner : ' + owner
        println 'delegate : ' + delegate
    }
    innerClosure.call()
}

outClosure.call()

delegate的指向可以被修改,修改后的delegate可与owner的只想不一样

class Person{}
def p = new Person();
def outClosure = {
    def innerClosure = {
        println 'this : ' + this
        println 'owner : ' + owner
        println 'delegate : ' + delegate
    }
    innerClosure.delegate = p
    innerClosure.call()
}
outClosure.call()

闭包的委托策略

class Student {
    String name
    def closure = {"my name is ${name}"}

    String toString() {
        println closure.call()
    }
}

class Teacher{
    String name
}

Student student = new Student(name: 'Tom');
Teacher teacher = new Teacher(name: 'Annie')

student.closure.delegate = teacher
student.closure.resolveStrategy = Closure.DELEGATE_FIRST

student.toString()

将student对象内closure的delegate修改为指向teacher,再将其委托策略改为delegate优先,那么闭包中使用到的属性会优先在delegate指向的对象中查找并使用,如果delegate指向的对象找不到要用的属性,再从owner指向的对象中查找。如果将委托策略改为DELEGATE_ONLY,则闭包只会在delegate指向的对象中查找属性,找不到则报错。

class Student {
    String name
    def closure = {"my name is ${name}"}

    String toString() {
        println closure.call()
    }
}

class Teacher{
    String nameX
}

Student student = new Student(name: 'Tom');
Teacher teacher = new Teacher(nameX: 'Annie')

student.closure.delegate = teacher
student.closure.resolveStrategy = Closure.DELEGATE_ONLY

student.toString()

以上例子可以发现,合理的修改闭包策略,可以实现通过student调用teacher中的方法或属性。

数据结构

groovy数据结构包括列表、映射和范围。
groovy中list结构类似于java数组,并且默认使用java.util.ArrayList。

def list = [1, 2, 3, 4, 5]
println list.class
println list.size()

由于list定义使用了中括号,所以在定义数组时需用用as关键字指定数组类型(或直接使用强类型进行定义)。

def array = [1, 2, 3, 4, 5] as int[]
println array.class
println array.length

int[] a = [1, 2, 3, 4, 5]

groovy在java集合的基础上对list新增了许多操作

  • list从小到大排序
    def list = [9, -2, 3, 7, 6, 1, -5]
    list.sort()  // 对list从小到大排序
    println list
    
  • list按绝对值从大到小排序
    def list = [9, -2, 3, 7, 6, 1, -5]
    // 使用闭包自定义排序规则
    list.sort{
        a,b -> a == b ? 0 : Math.abs(a) > Math.abs(b) ? 1 : -1
    }
    println list
    
  • 按字符串长度对字符串排序
    def list = ['java', 'groovy', 'python', 'scala', 'c++']
    // 使用闭包自定义排序规则
    list.sort { it.length() }
    println list
    
  • 列表的其他方法
    def list = [-3, 9, 6, 2, -7, 1, 5]
    // 查找第一个偶数
    println list.find { it % 2 == 0 }
    // 查找所有的偶数
    println list.findAll { it % 2 == 0 }
    // 判断列表是否存在偶数
    println list.any { it % 2 == 0 }
    // 判断列表是否全都为偶数
    println list.every { it % 2 == 0 }
    // 找出绝对值最大的数
    println list.max { Math.abs(it) }
    // 找出绝对值最小的数
    println list.min { Math.abs(it) }
    // 统计列表中偶数的个数
    println list.count { it % 2 == 0 }
    

映射的功能类似于java中的Map(实际上映射默认使用java.util.LinkedHashMap实现),但写法稍有不同。

// 定义一个映射
def colors = ['red': '#ff0000','green':'#00ff00','blue':'#0000ff']

使用中括号+key的形式可以获取到指定key对应的value值。

println colors['red']

也可以通过.操作符获取指定key对应的value。

println colors.red

添加(更新)操作也通过.操作符来完成。

colors.yellow = '#ffff00' // 添加新元素
colors.red = 'rgb(255,0,0)' //更新元素

值得注意的是,groovy 中的映射可以添加任意类型。

groovy基于Map的基础上,为映射添加了许多快捷操作。

// 定义一个映射
def students = [
        1 : ['number': '0001','name':'Bob','score':55,'sex':'male'],
        2 : ['number': '0002','name':'Johnny','score':62,'sex':'female'],
        3 : ['number': '0003','name':'Claire','score':73,'sex':'female'],
        4 : ['number': '0004','name':'Amy','score':66,'sex':'male'],
]
  • 遍历映射

    // each遍历映射
    students.each { student -> 
        println "key: " + student.key + "\tvalue: " + student.value
    }
    // 带索引形式遍历映射
    students.eachWithIndex { student, index ->
        println "index:" + index + "\tkey: " + student.key + "\tvalue: " + student.value
    }
    // 直接通过key,value遍历
    students.eachWithIndex { key, value, index ->
        println "index:" + index + "\tkey: " + key + "\tvalue: " + value
    }
    
  • 查找操作

    // 查找第一个及格的学生
    println students.find { it.value.score >= 60 }
    // 查找所有及格的学生
    println students.findAll { it.value.score >= 60 }
    // 统计及格男生的总人数
    println students.count {
        it.value.score >= 60
                && it.value.sex == 'male'
    }
    // 查找所有及格的学生,并返回姓名
    println students.findAll { it.value.score >= 60 }
            .collect { it.value.name }
    // 将及格,不及格学生进行分组
    println students.groupBy { def student -> student.value.score >= 60 ? '及格' : '不及格' }
    
  • 排序操作

    映射的排序与列表不同,列表sort方法会直接对原列表排序(原列表会发生改变), 映射sort方法会返回一个排序好的map(原映射数据不会改变)。

    // 根据分数排序
    println students.sort{def studentA, def studentB ->
        def scoreA = studentA.value.score
        def scoreB = studentB.value.score
        return scoreA==scoreB ? 0 : scoreA > scoreB ? 1 : -1
    }
    

范围是groovy新增的数据结构,通过数字..数字定义,使用中括号和下标的方式获取范围值。

// 定义一个范围
def range = 1..10
// 输出起始值
println range[0]

也可以通过范围的from属性和to属性获得范围的其实质和终止值。

println range.from // 输出起始值
println range.to // 输出终止值
  • 遍历范围

    范围的遍历可以通过for循环和闭包两种方式进行。

    // 定义一个范围
    def range = 1..10
    
    // for循环遍历
    for(def x in range) {
        println x
    }
    
    // 闭包遍历
    range.each {println it}
    
  • switch-case中使用范围

    def grade(Number score){
        def result
        switch(score) {
            case 0..<60: // <表示不包含60
                result = '不及格'
                break
            case 60..<70:
                result = '及格'
                break
            case 70..<85:
                result = '良好'
                break
            case 85..100:
                result = '优秀'
                break
        }
        return result
    }
    println grade(75)
    

面向对象基础

groovy中也可以定义类和接口,并且所有的类默认都是public类型的(内部的字段,方法默认也是public类型)。

/* 定义一个groovy类 */
class Person {
    String name
    Integer age
    
    def incAge(Integer year) {
        age += year
    }
}

groovy使用new关键字进行对象的实例化,实例化时可以指定属性的内容(此处与java不同,java需要提供带参数的构造方法)。

def person = new Person(name: 'Annie', age: 18 ) // 实例化一个Person对象
println "the name is ${person.name}, the age is ${person.age}"

通过实例对象.属性的方式调用,实际上调用的时java类中的setter、getter方法,尽管在Class中未手动定义。

groovy中对方法的调用与java中相同。

person.incAge(10)  // 调用incAge方法

groovy中接口的定义与java中相似(实际上是java接口去掉public关键字,因为所有的方法默认都是public,并且不允许使用其他范围的关键字)。

/* 定义一个groovy接口 */
interface Action {
    void eat()
    void drink()
    void play()
}

接口的使用也与java相同,类通过implements关键字实现接口并需要覆写所有方法。

/* 定义一个groovy类 */
class Person implements Action{
    @Override
    void eat() {}

    @Override
    void drink() {}

    @Override
    void play() {}
}

groovy中可以定义一个trait,trait具有java抽象类和接口的特性(是二者的结合),类通过implements关键字实现trait并覆写需要的方法(接口需要覆写所有方法),也就是说在trait中可以有方法默认的实现。

/* 定义一个trait */
trait DefaultAction {
    abstract void eat()

    void play() {
        println 'i can play !!!'
    }
}

面向对象高级

groovy中提供了元编程(在运行时期执行的代码)。

在java中调用一个方法,会在调用者所属的类及父类中寻找这个方法,如果找不到则编译器就会报错。

在groovy中调用一个方法,如果在其类及父类中找不到,则会查找MethodClass是否有此方法,如果有则调用MethodClass中的方法,如果没有则继续查看是否覆写了methodMissing方法,如果重写了methodMissing方法,则调用methodMissing,如果没有覆写则查看是否覆写了inkokeMethod方法,如果覆写了则调用,否则抛出异常(MissingMethodException)。

调用方法
类中是否有此方法
直接调用
类中方法
MethodClass
中是否有此方法
调用MethodClass
中的方法
否覆写了
methodMissing方法
调用
methodMissing方法
是否覆写了
inkokeMethod方法
调用
inkokeMethod方法
throw MissingMethodException
/* 定义一个groovy类 */
class Person {
    String name
    Integer age

    def incAge(Integer year) {
        age += year
    }
}

Person p = new Person();
print p.cry(); //调用类中不存在的方法

运行上面的代码发现程序抛出了groovy.lang.MissingMethodException异常,覆写了inkokeMethod方法后再次调用发现程序执行了inkokeMethod方法。

/* 定义一个groovy类 */
class Person {
    String name
    Integer age

    def incAge(Integer year) {
        age += year
    }

    @Override
    def invokeMethod(String name, Object args) {
        return "the method is ${name}, the parameters is ${args}"
    }
}

Person p = new Person();
print p.cry(); //调用类中不存在的方法

上述程序输出结果为:the method is cry, the parameters is []

class Person {
    String name
    Integer age

    def incAge(Integer year) {
        age += year
    }

    @Override
    def invokeMethod(String name, Object args) {
        return "the method is ${name}, the parameters is ${args}"
    }

    def methodMissing(String name, Object args) {
        return "the method ${name} is missing"
    }
}

Person p = new Person()
print p.cry() //调用类中不存在的方法

覆写了methodMissing方法后,程序输出:the method cry is missing

metaClass可以在运行时为类动态的添加属性和方法。

// 为类动态添加属性
Person.metaClass.sex = 'male'
Person p = new Person(name: 'Annie', age: 18)
println p.sex

// 为类动态添加方法
Person.metaClass.sexToUpCase = { -> sex.toUpperCase() }
println p.sexToUpCase()

除了上面事例中为类添加普通属性和方法外,还可以动态为类添加static方法。

// 为类动态添加static方法
Person.metaClass.static.create = {
    String name, Integer age -> new Person(name: name, age: age)
}
Person person = Person.create('Tom', 12)
println "my name is ${person.name} and age ${person.age}"

使用metaClass属性注入的属性和方法只在本脚本中有效,如果想要使注入的属相和方法全局生效,则需要设置ExpandoMetaClass全局可用。

ExpandoMetaClass.enableGlobally()

JSON操作

groovy支持将json转换为对象或将对象转换为json字符串。

  • 实体对象转换为JSON

    import groovy.json.JsonOutput
    
    class Person {
        String name
        int age
    }
    
    def list = [new Person(name: 'Tom', age: 15), new Person(name: 'Annie', age: 18)]
    def json = JsonOutput.toJson(list) //实体对象转换为JSON
    println JsonOutput.prettyPrint(json) // JSON格式化输出
    
  • JSON字符串转换为实体对象

    def json = '''[
            {
                "age": 15,
                "name": "Tom"
            },
            {
                "age": 18,
                "name": "Annie"
            }
    ]'''
    def jsonSlurper = new JsonSlurper()
    def object = jsonSlurper.parseText(json);
    println object
    println object.class
    

XML操作

在java语言中,对xml的处理主要有DOM文档驱动处理和SAX事件驱动处理两种方式。SAX解析采用逐行读入方式减少了解析事件,但相比于groovy对xml的处理,仍然是比较复杂的。

  • XML解析

    // 定义一个xml字符串
    def xmlText = '''<?xml version="1.0" encoding="UTF-8"?>
        <books>
          <book id="1" avaliable="20" >
            <title>Groovy从入门到精通</title>
            <author>李刚</author>
            <price>23.56</price>
          </book>
          <book id="2">
            <title>Gradle实践</title>
            <author>Annie</author>
            <price>80.91</price>
          </book>
          <book id="3">
            <title>Vue从入门到精通</title>
            <author>李刚</author>
            <price>26.91</price>
          </book>
        </books>
    '''
    def xmlSlurper = new XmlSlurper()  // xml解析器
    def books = xmlSlurper.parseText(xmlText) // 解析xml
    

    text()方法用于输出节点的文本,使用@+属性名的方式可以访问节点的属性。

    // 输出解析结果
    println books.book[0].title.text()
    println books.book[0].@id
    
    

    groovy提供了深度遍历方法来查找节点。(例如查找所有作者为李刚的书名)。

    // depthFirst()可以用'**'来代替
    def titles = books.depthFirst().findAll {
        book -> book.author.text() == '李刚' ? true : false 
    }
    println titles
    

    广度遍历与深度遍历的用法几乎一(找到id为2的书名)。

    // children()可以用'*'来代替
    def titles = books.children().findAll {
        book -> book.name() == 'book' && book.@id == 2
    }.collect{
        node -> node.title.text()
    }
    
    println titles
    
  • XML生成( 生成xml数据需要用到MarkupBuilder类)。

    def sw = new StringWriter()
    def xmlBuilder = new MarkupBuilder(sw) // 用来生成xml数据的核心类
    xmlBuilder.books(type: '计算机技术') {
        // 第一个book节点
        book(id:'1',avaliable:'20'){
            title('Groovy从入门到精通')
            author('李刚')
            price('23.56')
        }
        // 第二个book节点
        book{
            title('Gradle实践')
            author('Annie')
            price('80.91')
        }
        // 第三个book节点
        book{
            title('Vue从入门到精通')
            author('李刚')
            price('26.91')
        }
    }
    // 打印生成的xml
    println sw
    

文件操作

groovy在java流的基础上封装了许多操作文件的方法。

  • eachLine() 方法读取文件。

    // 读取文件
    def file = new File('E:/groovy.txt')
    file.eachLine {line -> println line}
    
  • getTex() 方法读取所有内容。

    def text = file.getText();
    println text
    
  • readLines() 方法读取每一行内容,以list形式返回。

    def textList = file.readLines()
    println textList
    
  • withReader() 方法可以读取文件指定部分的数据。

    def text = file.withReader {
        reader ->
            {
                char[] buffer = new char[100]
                reader.read(buffer)
                buffer
            }
    }
    println text
    
  • withWriter() 方法可以向文件写入数据。

    file.withWriter { writer ->
        {
            writer.write('code: 1DfGc4')
        }
    }
    
  • withObjectOutputStream() 方法可以将对象写入文件,withObjectOutputStream() 方法可以从文件中获取对象,使用它们可以实现对象的序列化和反序列化。

    class Person implements Serializable{
        String name
        Integer age
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    /**
     * 使用文件保存对象
     */
    def serialize = {
        String path, def obj ->
            {
                try {
                    def file = new File(path)
                    if (!file.exists()) { // 文件不存在,创建新文件
                        file.createNewFile()
                    }
                    // 将对象写入文件
                    file.withObjectOutputStream {
                        out -> out.writeObject(obj)
                    }
                } catch (Exception e) {
                    println e
                }
            }
    }
    
    /**
     * 从文件中读取对象
     */
    def deserialize = {
        String path ->
            {
                def obj = null
                try {
                    def file = new File(path)
                    if (!file.exists()) { // 文件不存在,创建新文件
                        return null
                    }
                    // 将对象写入文件
                    file.withObjectInputStream {
                        input -> obj = input.readObject()
                    }
                } catch (Exception e) {
                    println e
                }
                obj
            }
    }
    
    def person = new Person(name:'Annie',age:18)
    serialize('./person.bin',person)
    def p = deserialize('./person.bin')
    println person // 原对象
    println p // 从文件中读取出的对象
    println p==person 
    
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值