经过初步了解发现groovy这门语言,竟然和java无缝对接,真的快速上手。学习了几门语言发现:一切高阶的语言都是建立在底层语言的基础上的,比如kotlin在java的基础上进行了扩展封装。本文的groovy,也是对java的扩展封装。java的底层也c++实现的,因此有号称c+++的称号。有了几门语言基础学习其他的就快多了。。。
(1)变量类型
其实基本类型也是对象类型,Groovy吧基本类型编译为包装类型所以:
groovy的变量类型都是对象类型
(2)变量定义
(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
看下String类,方法贼多比原来的java的String多了好多。这些方法来自哪里呢?
(1)方法来源
(2)字符串三种定义方式及其区别
定义方式和区别
(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扩展的即可。
自上而下代码执行,没啥好多说的。
常见:
- 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
常见:
- while
- for(groovy对for进行了扩展)
(1)常见for循环方式
//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、 简单的定义调用
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
使用:都是通过方法的调用,吧闭包当做方法的参数。
理解:方法内部不需要关注具体的逻辑,只需要按照固定的模板调用即可,具体的实现交付给调用者实现。和接口回调类似。(看一带闭包参数的方法源码可以发现)
注意:闭包方法调用的入口(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
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() // 所有大写
}
(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)定义
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
}
(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]]]
范围其实就是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 extends List
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、类中 默认都是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("人要吃饭")
}
}
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() // 注入的方法或者成员全局可用
基础语法大略过了一遍,下节的文件处理,文件过了后就真正的接触到Gradle啦!期待一波。。。