Groovy核心语法

经过初步了解发现groovy这门语言,竟然和java无缝对接,真的快速上手。学习了几门语言发现:一切高阶的语言都是建立在底层语言的基础上的,比如kotlin在java的基础上进行了扩展封装。本文的groovy,也是对java的扩展封装。java的底层也c++实现的,因此有号称c+++的称号。有了几门语言基础学习其他的就快多了。。。

知识点

在这里插入图片描述

一、基本语法

1、变量

(1)变量类型

  • 基本类型
  • 对象类型

其实基本类型也是对象类型,Groovy吧基本类型编译为包装类型所以:
groovy的变量类型都是对象类型

(2)变量定义

  • 强类型:使用int、double、对象定义变量
  • 弱类型:使用def定义变量

(3)栗子

int a = 10
double b = 2.11
def c = 1.23 // 小数默认为大数据类型
println(a.class) //class java.lang.Integer
println b.class //class java.lang.Double
println c.class //BigDecimal
2、字符串方法来源

看下String类,方法贼多比原来的java的String多了好多。这些方法来自哪里呢?

(1)方法来源

  • java.lang.String:java原有的。
  • DefaultGroovyMethod:groovy对String的扩展类
  • StringGroovyMethod:继承 DefaultGroovyMethod,主要学习这个类的一些方法即可。

(2)字符串三种定义方式及其区别

定义方式和区别

  • 单引号方式:groovy中单引号也可以定义字符串与双引号一致。(这种没有格式与双引号一致,例如你换行时系统给你以加号拼接),不可以使用$变量或者表达式取值。
  • 双引号方式:定义字符串,可以使用$变量或者表达式取值(和kotlin语法一致)
  • 三个单引号方式:定义字符串,保留字符串内部的原始格式(如换行、书信格式、等格式)

(3)栗子

def s = "test"   // 弱语言类型
    s = 10      //自动修改推断类型
println(s.class) //Integer类型

def string = 'a string '
def three = ''' three''' 
println("$string") 

(4)StringGroovyMethod的一些常见方法

  • 普通方法(常用如下)
  • 结合闭包的方法(闭包中讲解)
//1、center 方法
def str= "groovy"
println str.center(8,"a")//agroovya  字符串居中,两边填充指定字符
println str.center(10)//字符串居中,两边填充空格
// padLeft方法。和center方法类似,只是从左边填充
//padRight方法。和center padLeft 类似。只是从右面填充

//2、比较方法、操作符
def str2 = "HAha"
println str2.compareTo(str) // 推荐使用  str2<=>str (这个运算符底层就是compareTo源码)
println(str2>str)


// 3、获取指定索引的字符
println str.getAt(0) // 写法1
println(str[0]) // 写法2 和kotlin类似
println(str[0..1]) // 还可以传范围

// 4 减法(去掉指定字符串)
println(str.minus("oo")) // 推荐使用减号 - 即  str-"oo"

//5、其他常见方法
println(str.reverse()) // 翻转
println(str.capitalize()) // 首字母大写
println(str.isNumber()) // 是否是数字类型的字符串
println(str.toInteger()) // 转换为指定类型  str.toXXX  (类型必须正确都是才可以转换)

二、逻辑控制

和java几乎一致,掌握下groovy扩展的即可。

1、顺序语句

自上而下代码执行,没啥好多说的。

2、条件语句

常见:

  • if-else
  • switch-case(groovy进行了扩展case语句可以为任意对象变量或者表达式)

/**
 * Created by sunnyDay on 2019/10/9 11:09
 */
// switch case  扩展    一句话case可以为任意类型的对象变量语句
def  a = 3.14159
def result

switch (a){
    
    
    case "hello"://字符串
        result = "fond hello"
        break
    case [1,2,3,"test"]://列表
        result = "fond list"
        break
    case 10..30: //范围区间(和kotlin写法一致,有kt基础这里会明白)
        result = "fond range"
        break
    case Short:
        result = "fond object type -> Short"
        break

    case BigDecimal:
        result = "fond object type -> BigDecimal"
        break

    default:
        result = "fond default value"
        break
}

println(result) //fond object type -> BigDecimal
3、循环语句

常见:

  • while
  • for(groovy对for进行了扩展)

(1)常见for循环方式

  • 范围循环(和kt一样)
  • list 循环 [元素1、元素2、、、] 就代表list
  • 对map循环 [key1:value1,key2,value2、、、、] 就代表map
//1、范围循环(和kt一样)
def sum = 0
for (i in 1..10){
    
    
    sum +=i
}
println(sum)

//2、list 循环  [元素1、元素2、、、] 就代表list
sum = 0
for (i in[1,2,3,4]){
    
    
    sum +=i
}
println(sum)

// 3、对map循环   [key1:value1,key2,value2、、、、] 就代表map
sum = 0
for (i in ["Tom":20,"Amos":24,"jerry":18]){
    
    
    // 这里可以直接获得map的 key 获得value
    sum+= i.value
}
println(sum)

三、闭包

1、闭包的基础知识

理解:闭包其实就是一段特殊代码块。使用{}来表示,只是这段代码块还可以传参数。接下来看下闭包的定义调用闭包参数闭包返回值

//1、 简单的定义调用
def clouser = {
    
     println("我是闭包") }
clouser.call() // 闭包调用方式1
clouser()// 闭包调用方式2

// 2、参数传递
// 定义(多个参数定义时,使用逗号隔开即可)
def test  = {
    
    String name ->println "我是带参数的闭包,参数值为:$name"}   // 使用-> 定义有参数的闭包,符号左边为参数定义,右边为闭包体。
// 调用
test.call("Tom") // 方式1:call 传参数即可
test("Kate") // 方式2:传参即可


// 3 闭包的默认参数  it
def defPar = {
    
    
    println("使用默认参数it,值为:$it")
}
defPar.call("default")  // 怎样更改默认参数?用户只需显式指定个参数即可。


//4、返回值
def returnValue = {
    
    
    return "我是返回值"
}
println(returnValue()) // 接收return的返回值


def returnNull = {
    
    
    println("不写return时返回:")
}
println(returnNull()) // 可以接收空返回值类型,返回结果为Null
2、闭包的使用

使用:都是通过方法的调用,吧闭包当做方法的参数。
理解:方法内部不需要关注具体的逻辑,只需要按照固定的模板调用即可,具体的实现交付给调用者实现。和接口回调类似。(看一带闭包参数的方法源码可以发现)
注意:闭包方法调用的入口(call()等),有助理解闭包结合各类型的使用。

(1)结合基本类型

常见方法:
1、upto
2、downto
3、times

// 结合基本类型

// 求指定数字的阶乘 upto方式
int fab(int number) {
    
    
    int result = 1
    1.upto(number, {
    
     num -> result *= num })  // 循环在upto内操作了(数字递增到number)
    return result
}
println(fab(5))

// 使用downTo方式
int fab2(int number) {
    
    
    int result = 1
    number.downto(1) {
    
     num -> result *= num }// ,作为方法参数的最后一个时,闭包还可以放大括号外,
    // 闭包放方法外是groovy中常见的方式(number递减到数字)
    return result
}
println(fab2(5))

// 累积求和
int sum(int number) {
    
    
    int result = 0
    number.times {
    
     // times 内部循环是从0开始的
        num -> result += num
    }
    return result
}

println(sum(10))

结合上栗子对upto源码理解:

 public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
    
    
        int self1 = self.intValue(); // self1对象为上文定义的1包装类Integer类型
        int to1 = to.intValue(); // to1为用户传递的数字参数的包装类型
        if (self1 <= to1) {
    
    
            for (int i = self1; i <= to1; i++) {
    
     //相当于 i=1;i<number;i++
                closure.call(i);
            }
        }

(2)字符串与闭包结合

常用方法:
1、each:字符串的遍历方法,返回值为调用者类型T
2、find:查找指定第一个满足闭包条件字符串方法 ,返回值为调用者对象类型Object。( 看源码发现find的闭包必须为boolean 类型的返回值)
3、findAll: 返回指定的所有字符串,返回一个collection集合。
4、any:字符串是否满足某种条件,满足时返回true (对应方法为every,是否每一个字符串都满足某种条件,any 方法需要返回boolean类型的闭包)
5、collection:同一处理字符串


// 字符串的遍历 each方法
String s = "i am String 1"
s.each {
    
     //返回值就是调用者本身
    String temp-> print(temp)
}


// find方法  查找指定第一个,满足条件字符串
// 看源码发现find的闭包必须为boolean 类型的返回值
println s.find{
    
     
    String temp -> temp.isNumber() // 第一个数字
}
// 对应方法 findAll 返回指定的所有字符串(返回一个collection集合)


// any 方法。   需要返回boolean类型的闭包
//字符串是否满足某种条件,有满足时返回true (对应方法为every,是否每一个字符串都满足)
println s.any{
    
    
    num->num.isNumber()
}


// collection   同一处理字符串
println s.collect{
    
    
    it.toUpperCase() // 所有大写
}
3、闭包的进阶

(1)闭包中关键量

this:闭包定义处的类(不指向闭包对象)
owner:闭包定义处的类或者对象(最近的类,可以指向闭包对象
delegate:代表任意对象,默认与owner一致。(最近的类,可以指向闭包对象
ps:这三个关键量在闭包中有如上意思,仅仅存在闭包中才有如上含义!!!

(2)三个关键量的区别

1、一般情况下或者方法中定义闭包:三者值一致。
2、当闭包套闭包时,这时owner和delegate 就代表最近的闭包对象了。
3、修改delagate值可以使默认的delegate与owner值不一致。
ps:this:、owner不能修改。delegate可以修改。

package variable
import com.sun.corba.se.spi.ior.IdentifiableFactory
/**
 * Created by sunnyDay on 2019/10/9 19:20
 *
 */
//clouserPlus.groovylei 源文件中

// 1、类中或者方法中定义
def close = {
    
    
    println("闭包"+this)
    println("闭包"+owner)
    println("闭包"+delegate)
}
close.call()
/*
log:clouserPlus的对象内存地址
闭包variable.clouserPlus@58d75e99
闭包variable.clouserPlus@58d75e99
闭包variable.clouserPlus@58d75e99
*/


// 2、闭包中执行闭包
def close1 = {
    
    

    def close2 ={
    
    
        println("闭包中的闭包:"+this) // 闭包定义处的类(不可指向闭包对象)
        println("闭包中的闭包:"+owner) // 指向最近的对象(可以指向闭包对象)
        println("闭包中的闭包:"+delegate)//默认情况下指向最近的对象(可以指向闭包对象)
    }
    close2.call()
}

close1.call()

/*
闭包中的闭包:variable.clouserPlus@194bcebf
闭包中的闭包:variable.clouserPlus$_run_closure2@32502377
闭包中的闭包:variable.clouserPlus$_run_closure2@32502377
*/

// 3、修改delegate的默认指向
def close3 = {
    
    

    def close4 ={
    
    
        println("修改delegate:"+this)
        println("修改delegate:"+owner)
        println("修改delegate:"+delegate) //打印数值和owner不在一致
    }
    close4.delegate = new clouserPlus()
    close4.call()
}
close3.call()
/*修改delegate:variable.clouserPlus@53ce1329
修改delegate:variable.clouserPlus$_run_closure3@67f639d3
修改delegate:variable.clouserPlus@6253c26
*/

(3)闭包的委托策略

1、修改delegate的指向。
2、修改委托策略。默认为OWNER(Closure.OWNER_FIRST)指向的对象。

// 闭包的委托策略

class Stu {
    
    

    String name
    public Stu(String name) {
    
    
        this.name = name
    }

    def setName = {
    
     println("My name is $name") }
    void printName() {
    
    
        setName.call()
    }

}

class Per{
    
    
    String name
    public Per(String name) {
    
    
        this.name = name
    }
}

def stu = new Stu("Tom")
def per = new Per("Jerry")
// 1、正常情况下
stu.printName()//My name is Tom

// 2、更改闭包中delegate的指向
stu.setName.delegate = per
// 修改委托策略
stu.setName.resolveStrategy=Closure.DELEGATE_FIRST// 默认为OWNER(OWNER_FIRST)指向的对象,此句代码注释。则结果还是My name is Tom
stu.printName()// My name is Jerry

1、理解:调用的还是原类中的方法,只是闭包内的逻辑发生了改变。
2、其他策略:

  • DELEGATE_FIRST
  • OWNER_FIRST
  • OWNER_ONLY
  • DELEGATE_ONLY

3、 注意:两类中name名字一致,如上DELEGATE_FIRST则优先找per对象的name,没有再找stu对象的name。如果Per类中定义的为name1而不是name,找不到还是会去Stu的name去寻找。这就是优先的意思。

四、数据结构

1、列表

(1)定义

def list = [1, 2, 3, 4, 5] // 定义了个list 默认为arrayList
println(list.size()) //5
println(list.class) //class java.util.ArrayList


def array = [1, 2, 3] as int[]  // 数组
println(array.class)

int[] arr2 = [1, 2, 3]
println(arr2.class)

注意:
1、list 默认为arrayList
2、如上的定义方式占用了数组的方式,如何定义数组?

1、只需使用as 关键字强转即可。
2、使用强类型定义

(2)列表常用方法(一般结合闭包)

1、sort
2、 min最小值
3、 max 最大值
4、 count 统计
5、findAll
6、any 存在满足
7、every 每个满足
8、each 遍历
ps:这些也可以传参闭包Closure,指定自己的规则

// 1、列表排序
def sortList = [1, 3, 5, 2, 10, 8, -5, 4]
/*
java 提供了两种快速方式:
Collections.sort(list)
Collections.sort(sortList,Comparator) // 自定义比较器方式
*/
println sortList.sort()// groovy 提供1,默认从小到大。

println sortList.sort {
    
    
    a, b ->
        a == b ? 0 :
                Math.abs(a) > Math.abs(b) ? 1 : -1
} // 自定义比较器方式,参数为闭包。(这里按照绝对值大小排序)

// 2、查找(主要看结合闭包的方法)
def findList = [1, 3, 5, 8, "a"]
println findList.find {
    
     //这里查找第一个出现的偶数
    return it % 2 == 0
}
2、映射

(1)定义

// 定义: [key1:value1,key2,value2、、、、] 就代表map
def map = [red: "red", green: "green", blue: "blue"]

// value的访问
println map.red //方式1(常用)
println map["red"]//方式2

// 设置value(找不到对应的键时,会把新的键值对添加到map)
map.red = "new red"
map.black = "black" // 找不到对应的键时,会把新的键值对添加到map。

println(map.toMapString())
println(map.getClass()) // class java.util.LinkedHashMap 默认,不想默认就显示声明,或者使用as转换。
// 这里为啥使用getClass,而不是.class 因为使用点class时会寻找键为class的map键值对。

注意点:
1、定义时key尽量使用String或者Number类型(默认为单引号不可变字符串)
2、默认的map类型为:class java.util.LinkedHashMap ,不想默认就显示声明,或者使用as转换。
3、这里为啥使用getClass,而不是.class ?因为使用点class时会寻找键为class的map键值对。

(2)map的常用操作

1、和List几乎一致(each、any、等等)
2、分组 groupby方法(类似sql的groupby)

def student = [1: [name: "Tom", age: 18],
               2: [name: "Jerry", age: 23],
               3: [name: "Kate", age: 18]]

// 1、遍历 each
println student.each {
    
    
        //闭包方式遍历
    def stu -> println("name:" + stu.key + "age:" + stu.value)
}
//2、直接遍历:key  value
println student.each {
    
    
        
    key, valu -> println("name:" + key + "age:" + valu)
}

//分组  groupby
println(student.groupBy {
    
    
    def stu -> return stu.value.age > 20 ? "成年" : "未成年"
}) //[未成年:[1:[name:Tom, age:18], 3:[name:Kate, age:18]], 成年:[2:[name:Jerry, age:23]]]
3、范围

范围其实就是Range类,进入源码发现其为List的子类。groovy中可用 num1..num2表示。

(1)主要掌握

1、单个元素获取
2、遍历方式

  • each(闭包方式groovy推荐)
  • for

3、结合switch(switch case 的case语句可以为范围,满足条件的变量即可执行case语句。)

def range = 10..20 // Range 类为list的子类:public interface Range<T extends Comparable> extends List<T>

println(range[0])
// from 、to获得对应收尾元素
println(range.from)
println(range.to)

// 遍历方式:1、闭包each 2、使用for
println("each style :")
println(range.each {
    
    
    print(it+" ")
})
println("for style : ")
for (i in range){
    
    
   print(i+" ")
}

五、面向对象

面向对象和java几乎一致,这里总结下小细节的不同。

1、类、接口的小差异

(1)类

1、类中 默认都是public
2、类默认继承GroovyObject
3、def修饰方法,表示默认返回object类型。
4、没有指定构造,在使用构造时可以使用带参数的构造。
5、groovy类中,无论你是对象.字段调用,还是直接调用get、set方法,最终都是调用get、set方法(GroovyObject的方法)

/**
 * Create by SunnyDay on 2019/10/13
 */
class Person {
    
    
      String name
      int age

      def grow(){
    
    
          println("又涨了一岁")
      }
}

//----------------------------------------------
def person = new Person(name: "Tom",age: 18)
println("name:${person.name}  age:${person.age}")
person.eat()

(2)接口

接口中不可定义非public的方法。其他和java一致。

(3)trait

1、介绍:新的类型、和class、interface并列(参考下图和代码)
2、简介:和接口类似、更像抽象类。但是可以有默认实现。(一般开发中使用较少)
3、功能:类似适配器模式,模板模式。
4、使用和接口使用一样implements即可。

在这里插入图片描述

package objectstudy

/**
 * Create by SunnyDay on 2019/10/13
 */
trait Action {
    
    
    abstract run()

    void eat(){
    
    
        println("人要吃饭")
    }
}
2、元编程

1、元编程:编写代码执行的时期,例如:解释执行的js、编译执行的java、运行时执行的代码java反射。
2、groovy提供了了强大的运行时执行代码功能

(1)groovy 方法运行时的调用流程

在这里插入图片描述

1、如果是java直接执行代码,类中没有方法时直接编译就不通过。
2、没有方法时的执行检查顺序,自上而下。
3、也就是查找MetaClass类->查找methodMissing方法->查找invokeMethod方法。碰见存在的就不执行下面的了。

方法重写:

package objectstudy

/**
 * Create by SunnyDay on 2019/10/13
 */
class Person implements Action{
    
    
      String name
      int age

      def grow(){
    
    
          println("又涨了一岁")
      }

    @Override
    def run() {
    
    
        return null
    }

    /**
     * 一个方法找不到时会调用这个方法:用途框架中给出丢失方法提示。
     *@param name 要调用的方法名
     * @param args 要调用的方法参数
     * */
    @Override
    Object invokeMethod(String name, Object args) {
    
    
        return "你调用的方法名:$name,方法参数:$args"
    }

    /**
     * 二者存在时优先调用这个
     * */
    Object methodMissing(String name, Object args){
    
    
        return "你调用的方法名:$name,is Missing"
    }

}

//---------------------------------

1、如上代码,执行不存在的方法cry时:
new Person().cry()
2、编译时不报错
3、运行时打印的log:你调用的方法名:cry,方法参数:[]

(2) metaClass:为类动态添加方法、属性

动态添加字段:

// 类名.metaClass.要添加的字段 = “初始值” 
  Person.metaClass.sex = "male" // 为类动态添加属性
  println(new Person().sex)

ps:根据初始值便可推断出类型,不必定义字段类型。

动态添加方法:

// 类名.metaClass.方法名=闭包
 Person.metaClass.strUpCase = {
    
    
     String str ->str.toUpperCase()
  }
   println new Person().strUpCase("string")

1、方法为闭包即可,闭包内实现方法逻辑

添加静态方法或者字段

和普通的动态类似。只是多了个static关键字 Person.metaClass.static.方法\成员

(3)groovy运行时的使用场景

栗子:java如有个第三方的类 你想扩展他的某一个方法时:
1、不是final类,final方法时你可继承重写。
2、final类gg,不能继承源码,不能扩展他。
3、这时可以使用groovy中的metaClass.
ps:要想使动态添加的字段、方法全局可用,先添加此句代码(如下)申明下,再动态添加。ExpandoMetaClass.enableGlobally() // 注入的方法或者成员全局可用

The end

基础语法大略过了一遍,下节的文件处理,文件过了后就真正的接触到Gradle啦!期待一波。。。

猜你喜欢

转载自blog.csdn.net/qq_38350635/article/details/102465216