- Groovy是基于jvm虚拟机的一种动态脚本语言,完全兼容java,又在此基础上增加了很多动态类型和灵活的特性
- groovy 命令类似于 Java 中的 java 命令,用于执行 groovy Class 字节码文件;
- groovyc 命令类似于 Java 中的 javac 命令,用于将 groovy 源文件编译成 groovy 字节码文件;
- groovysh 命令是用来解释执行 groovy 脚本文件;
- 下面是 Groovy 中所有的关键字,命名时尤其需要注意:
as、assert、break、case、catch、class、const、continue、def、default、do、else、enum、extends、false、finally、for、goto、if、implements、import、in、instanceof、interface、new、null、package、return、super、switch、this、throw、throws、trait、true、try、while
- groovy中句末的分号不是必须的,函数调用的时候还可以不加括号。
如果当前这个函数是 Groovy API 或者 Gradle API 中比较常用的,比如 println,就可以不带括号。否则还是带括号。不然,Groovy 可能会把属性和函数调用混淆。
- 在 Groovy 中,== 相当于 Java 的 equals,,如果需要比较两个对象是否是同一个,需要使用 .is()
- Groovy 中函数的返回值也可以是无类型的,并且无返回类型的函数,其内部都是按返回 Object 类型来处理的
- 所有的 Class 类型,都可以省略 .class
- 非运算符: assert (!"android") == false
- 支持 ** 次方运算符: assert 2 ** 3 == 8
- 判断是否为真: if (android) {}
- 三元表达式: def result = name ?: "Unknown"
- 非空判断: println order?.customer?.address
- 使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常
- 使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦
- switch 方法可以同时支持更多的参数类型
// 输出 ok
def num = 5.21
switch (num) {
case [5.21, 4, "list"]:
return "ok"
break
default:
break
}
变量
- 变量类型 Groovy 中的类型同 Java 一样,也是分为两种:基本类型,对象类型。
但是,其实 Groovy 中并没有基本类型,Groovy 作为动态语言, 在它的世界中,所有事物都是对象,就如 Python、Kotlin 一样:所有的基本类型都是属于对象类型。
- 变量的定义有两种方式
- 强类型定义方式:即像 Java 一样,在声明变量的时候定义它的类型。
- 弱类型定义方式:通过 def关键字来定义我们任何的变量,因为编译器会根据值的类型来为它进行自动的赋值。
如果这个变量就是用于当前类或文件,而不会用于其它类或应用模块,那么,建议使用 def 类型,因为在这种场景下弱类型就足够了。 但是,如果你这个类或变量要用于其它模块的,建议不要使用 def,还是应该使用 Java 中的那种强类型定义方式,因为使用强类型的定义方式,它不能动态转换为其它类型,它能够保证外界传递进来的值一定是正确的。
字符串
- 单双引号都可以定义一个字符串常量,但单引号不能对字符串常量里的表达式做运算
task groovyString{
doLast{
def name="abc"
println '单引号的变量计算:${name}'
println "双引号的变量计算:${name}"
println "$name" //只有一个变量时可省略大括号
println "${1+2*3}"
}
}
gradle groovyString
> Task :groovyString
单引号的变量计算:${name}
双引号的变量计算:abc
abc
7
Groovy 中还新增了一个 GString 类型(当双引号种包含表达式运算时,打印其类型就是GString),编译器可以帮我们自动在 String 和 GString 之间相互转换,我们在编写的时候并不需要太过关注它们的区别。
集合
- List,Set,Map,Queue
task printList {
def numList=[10,11,12,13,14]
println numList.getClass().name
println numList[0]
println numList[1]
println numList[-1]
println numList[1..3]
numList.each{
println it
}
}
$ gradle printList
> Configure project :
java.util.ArrayList
10
11
14
[11, 12, 13]
10
11
12
13
14
task printMap{
def map1=['width':1080,'height':1920]
println map1.getClass().name
println map1['width']
println map1.height
map1.each{
println "key=${it.key}, value=${it.value}"
}
}
$ gradle printMap
> Configure project :
java.util.LinkedHashMap
1080
1920
key=width, value=1080
key=height, value=1920
方法
- 括号可以省略,如println 'abc'
- return可以省略,把最后一句当作返回值
task printMethodReturn{
def add1=method1 1,2
def add2=method1 6,4
println "add1=$add1,add2=$add2"
}
def method1(int a,int b){
if(a>b)
a
else
b
}
$ gradle printMethodReturn
> Configure project :
add1=2,add2=6
- 代码块可以作为参数传递
task printEach{
def list=[11,12,13,14]
//下面几种打印it写法
list.each({println it})
list.each({
println it
})
//方法的最后一个参数是闭包可以放到方法外面
list.each(){
println it
}
//方法可以省略括号
list.each{
println it
}
}
Groovy 面向对象
如果不声明 public/private 等访问权限的话,Groovy 中类及其变量默认都是 public 的;
JavaBean
- 可省略setter/getter方法,通过setter/getter也可以代替成员变量的声明
task printJavaBean{
Person p=new Person()
println "name=$p.name"
p.name="bob"
println "name=$p.name"
println "age=$p.age"
}
class Person{
private String name
public int getAge(){
28
}
}
gradle printJavaBean
> Configure project :
name=null
name=bob
age=28
元编程(Groovy 运行时)
脚本中的变量和作用域
对于每一个 Groovy 脚本来说,它都会生成一个 static void main 函数,main 函数中会调用一个 run 函数,脚本中的所有代码则包含在 run 函数之中
当我们在 Groovy 脚本中定义一个变量时,由于它实际上是在 run 函数中创建的,所以脚本中的其它方法或其他脚本是无法访问它的。这个时候,我们需要使用 @Field 将当前变量标记为成员变量
import groovy.transform.Field;
@Field author = JinYang
闭包
类似lambda表达式
task helloClosure{
customEach{
println it
}
eachMap {k,v ->
println "$k is $v"
}
}
def customEach(closure){
for(int i in 1..10){
closure(i)
}
}
//向闭包传参数
def eachMap(closure){
def map=['name':'zhangsan','age':18]
map.each{
closure(it.key,it.value)
}
}
groovy支持闭包方法的委托,其闭包有三个属性thisObject,owner,delegate, 调用闭包方法时,由他们确定使用哪个对象来处理
task helloDelegate{
new Delegate().test{
println "thisObject:${thisObject.getClass()}"
println "owner:${owner.getClass()}"
println "delegate:${delegate.getClass()}"
method1()
it.method1()
}
}
def method1(){
println "Context this:${this.getClass()} int root"
println "method1 in root"
}
class Delegate{
def method1(){
println "Delegate this:${this.getClass()} in Delegate"
println "method1 in Delegate"
}
def test(Closure<Delegate> closure){
closure(this)
}
}
$ gradle helloDelegate
thisObject:class build_d0vx8g4wj498jzsd4g4xes7j2
owner:class build_d0vx8g4wj498jzsd4g4xes7j2$_run_closure11
delegate:class build_d0vx8g4wj498jzsd4g4xes7j2$_run_closure11
Context this:class build_d0vx8g4wj498jzsd4g4xes7j2 int root
method1 in root
Delegate this:class Delegate in Delegate
method1 in Delegate
DSL中,比如Gradle,我们一般指定delegate为当前的it,这样我们在闭包内就可以对该it进行配置,或调用其方法
task configClosure{
dog{
dogName="bob"
dogAge=7
dumpDog()
}
}
class Dog{
String dogName
int dogAge
def dumpDog(){
println "name is $dogName,age is $dogAge"
}
}
def dog(Closure<Dog> closure){
Dog d=new Dog()
closure.delegate=d
//委托模式优先
closure.setResolveStrategy(Closure.DELEGATE_FIRST)
closure(d)
}
DSL
领域特定语言,在专而不在全,如Gradle是基于groovy,专门解决自动化构建的DSL
文件处理
读取文件
- eachLine
task readLine{
def file =new File("./info.txt")
file.eachLine{ String oneLine ->
println oneLine
}
def text=file.getText()
println text
def text2=file.readLines()
println text2
file.eachLine{oneLine,lineNo ->
println "${lineNo} --> ${oneLine}"
}
}
$ gradle readLine -b file.gradle
> Configure project :
zhangsan
lisi
wangwu
zhaoliu
zhangsan
lisi
wangwu
zhaoliu
[zhangsan, lisi, wangwu, zhaoliu]
1 --> zhangsan
2 --> lisi
3 --> wangwu
4 --> zhaoliu
- InputStream 也可以通过流的方式进行文件操作
//操作 ism,最后记得关掉
def ism = targetFile.newInputStream()
// do sth
ism.close
//利用闭包来操作 inputStream,其功能更加强大,推荐使用这种写法
targetFile.withInputStream{ ism ->
// 操作 ism,不用 close。Groovy 会自动替你 close
}
写入文件
- 通过 withOutputStream/、withInputStream copy 文件
task writeFile{
def srcFile=new File('./info.txt')
def targetFile=new File('./copyInfo.txt')
targetFile.withOutputStream{ os->
srcFile.withInputStream{ ins->
//利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream //的输出
os << ins
}
}
}
- 通过 withReader、withWriter copy 文件
task writeFile2{
try{
def srcFile=new File('./info.txt')
def targetFile=new File('./copyInfo.txt')
if(!targetFile.exists()){
targetFile.createNewFile()
}
srcFile.withReader{reader ->
def lines = reader.readLines()
targetFile.withWriter{writer ->
lines.each{line ->
writer.append(line + "\r\n")
}
}
}
return true
}catch(Exception e){
e.printStackTrace()
}
return false
}
此外,我们也可以通过 withObjectOutputStream/withObjectInputStream 来保存与读取 Object 对象
//保存对象到文件中
task saveObject(){
Person p=new Person()
p.name="alan"
p.age=18
try{
def desFile=new File("./person.txt")
if(!desFile.exists()){
desFile.createNewFile()
}
desFile.withObjectOutputStream{ out ->
out.writeObject(p)
}
return true
}catch(Exception e){
e.printStackTrace()
}
return false
}
//从文件中读取Object
task readObject(){
println "readObject start"
def obj=null
try{
def file=new File("./person.txt")
if(file==null || !file.exists())
println "file==null"
return null
file.withObjectInputStream{input ->
obj=input.readObject()
if(obj!=null){
println "name=$obj.name"
println "age=$obj.age"
}else{
println "obj==null"
}
}
}catch(Exception e){
e.printStackTrace()
}
println "readObject end"
}
class Person{
private String name
private int age
}
//读写的对象需要支持序列化,否则会报java.io.NotSerializableException