groovy的执行有两种方式
1.先将groovy文件编译(用groovyc编译器)为class类,之后交由JVM加载执行(这种方式跟JAVA没什么太大的区别)
2.在运行时生成groovy类,直接通过groovy Myscript.groovy来运行这个脚本,那么其执行的步骤具体来说有:
(1)Myscript.groovy被传递给groovy的转换器
(2)转换器产生了一个抽象语法树(AST)来表达在Myscript.groovy中的所有代码。
(3)groovy类生成器根据AST产生JAVA字节码,根据脚本的内容,结果可能是多个类,现在类通过groovy类加载器是可以使用的了。
(4)java运行时像调用一个java类Myscript一样来调用第三步产生的类。
第二种的方式执行如果不从这个步骤来看,表象是解释执行的一样,而事实上他在背后执行了如上的一些动作,这些类在使用之前已经完整的构建并且在运行时不能进行更改。
说groovy是动态语言,但groovy类是一次性产生并且在加载之后不能改变字节码的,类不能改变,那怎么能增加方法呢?groovy类生成器生成的字节码必然不同于java编译器生成的字节码(不是在格式方面,而是在内容方面),假设一个groovy文件包括一个像foo语句,groovy产生的字节码不是直接调用这个方法,而是这样:
getMetaClass().invokeMethod(this, "foo", EMPTY_PARAMS_ARRAY)
也就是说,方法的调用被重定向为通过对象的MetaClass进行处理,这个MetaClass现在能在运行时处理如拦截,重定向,增加/删除方法等之类的方法调用。这个原则运用到所有通过groovy代码的处理。
动态代码的第二个选项是把代码放在一个字符串中并且通过groovy来运行它,如
def code = '1 + 1.5'
println code //1 + 1.5
println evaluate(code) //2.5
groovy中不存在原始类型,一切都是对象,在JVM中,这些原始类型对应的对象是通过JAVA平台已经存在的包装类型进行包装的。
操作符重写:
class Money{
private int amount;
Money(amountValue){
amount = amountValue
}
Money plus(Money other){
if(null == other) return null
return new Money(amount + other.amount)
}
def buck = new Money(2)
assert buck + buck == new Money(4) //pass
JAVA中判断两个对象值是否相等,是通过equals,而groovy中则用==
而判断两个对象是否是同一个引用,JAVA是通过==,而groovy中则用is
特别的例子:def abc1 = 'abc' def abc2 = ‘abc’ println abc1.is(abc2) 返回true,这是因为这与JAVA中的String一样,缓存在string常量池的,两者指向相同的引用。
GDK也定义了times,upto,downto,step方法
def store = ''
10.times{
store += 'x'
}
assert store == 'xxxxxxxxxx'
store = ''
1.upto(5) { number ->
store += number
}
assert store == '12345'
store = ''
2.downto(-2) { number ->
store += number + ' '
}
assert store == '2 1 0 -1 -2 '
store = ''
0.step(0.5, 0.1){ number ->
store += number + ' '
}
assert store == '0 0.1 0.2 0.3 0.4 '
range的使用:
assert (0..10).containes(0)
assert (0..10).containes(5)
assert (0..10).containes(10)
assert (0..10).containes(-1) == false
assert (0..10).containes(11) == false
assert (0..<10).containes(9)
assert (0..<10).containes(10) == false
def a = 0..10
assert a instanceof Range
assert a.contains(5)
a = new IntRange(0, 10)
assert a.contains(5)
assert (0.0..1.0).contains(0.5)
def today == new Date()
def yesterday = today - 1
assert (yesterday..today).size() == 2
assert ('a'..'c').contains('b')
def log =''
for (element in 9..5){
log += element
}
assert log == '98765'
log = ''
(9..<5).each{ element ->
log +=element
}
assert log == '9876'
age = 36
switch(age){
case 16..20 : rate = 0.05; break
case 21..50 : rate = 0.06; break
case 51..65 : rate = 0.07; break
default : throw new Exception()
}
assert rate == 0.6
ages = [20,36,42,56]
midage = 21..50
assert ages.grep(midage) == [36,42]
上面的例子中,使用了date和String类型的范围,其实,range可以使用任何类型,只要这个类型满足以下两个条件:
1. 实现了next和previous方法,也就是说,重写++和--操作符
2. 实现了java.lang.Compareable接口,也就是说实现了compareTo方法,实际上是重写<==>操作符。
list通过index处理:
myList = ['a','b','c','d','e','f']
assert myList[0..2] == ['a','b','c']
assert myList[0,2,4] == ['a','c','e']
myList[0..2] = ['x','y','z']
assert myList == ['x','y','z','d','e','f']
myList[3..5] = []
assert myList == ['x','y','z']
myList[1..1] = ['y','1','2']
assert myList == ['x','y','1','2','z']
groovy里的索引也可以是负数,从负数索引是从列表的最后往前周。如
list = [0,1,2,3,4], 则list[-1] = 4, list[-2] = 3, list[4..0] =
[4,3,2,1,0], list[-2..2] = [3,4,0,1,2],不过一般为避免迷惑,不要这么去用。
除了通过索引操作List,还可以通过方法操作:
myList = []
myList += 'a'
assert myList == ['a']
myList += ['b','c']
assert myList == ['a','b','c']
myList = []
myList << 'a' << 'b'
assert myList == ['a','b']
assert myList - ['b'] == ['a']
assert myList * 2 == ['a','b','a','b']
GDK增加了额外的方法到List接口,Collection接口和Object类上。
list里的一些方法:
assert [1,[2,3]].flatten() == [1,2,3]
assert [1,2,3].intersect([4,3,1]) == [3,1]
assert [1,2,3].disjoint([4,5,6])
list = [1,2,3]
popped = list.pop()
assert popped == 3
assert list == [1,2]
assert [1,2].reverse() == [2,1]
assert [3,1,2].sort() == [1,2,3]
def list = [[1,0],[0,1,2]]
list = list.sort{a,b -> a[0] <=>b[0]}
assert list == [[1,0],[0,1,2]]
list = ['a','b','c']
list.remove(2)
assert list == ['a','b']
list.remove('b')
assert list == ['a']
list = ['a','b','b','c']
list.removeAll(['b','c'])
assert list == ['a']
def odd = [1,2,3].findAll{ item ->
item%2 == 1
}
assert odd == [1,3]
def x = [1,1,1]
assert [1] == new HashSet(x).toList()
assert [1] == x.unique
def x = [1,null,1]
assert [1,1] == x.findAll{it != null}
assert [1,1] == x.grep{it}
list遍历:
def list = [1,2,3]
assert list.count(2) == 1
assert list.max() == 3
assert list.min() == 1
def even = list.find{ item ->
item%2 == 0
}
assert even == 2
assert list.every{item -> item < 5}
assert list.any{item -> item < 2}
def store = ''
list.each{ item ->
store += item
}
assert store == '123'
store = ''
list.reverseEach{ item ->
store += item
}
assert store == '321'
assert list.join('-') == '1-2-3'
result = list.inject(0){ clinks, guest ->
clinks += guests
}
assert result == 0 + 1+2+3
assert list.sum() == 6
map的取值使用:
def myMap = [a:1,b:2,c:3]
assert myMap['a'] == 1
assert myMap.a == 1
assert myMap.get('a') == 1
assert myMap.get('a',0) == 1
assert myMap['d'] == null
assert myMap.d == null
assert myMap.get('d') == null
map的其他方法:
def myMap = [a:1,b:2,c:3]
def other = [b:2,c:3,a:1]
assert myMap == other
assert myMap.isEmpty() == false
assert myMap.size() == 3
assert myMap.containsKey('a')
assert myMap.containsValue(1)
assert myMap.keySet() == toSet(['a','b','c'])
assert toSet(myMap.values()) == toSet([1,2,3])
assert myMap.entrySet() instanceof Collection
assert myMap.any {entry -> entry.value > 2}
assert myMap.every {entry -> entry.key < 'd'}
map的遍历:
def myMap = [a:1,b:2,c:3]
myMap.each{ entry ->
//entry.key
//entry.value
}
myMap.each{key,value ->
//key
//value
}
for(key in ,myMap.keySet()){
//key
}
for(value in ,myMap.values()){
//value
}
面向对象的最高原则是对象有自己的行为和数据,闭包也是对象,他主要的目的是他们的行为。
一个闭包是被包装为一个对象的代码块,实际上闭包像一个接受参数并且能够有返回值的方法。闭包是一个普通对象,因为你能够通过一个变量引用到它,正如你能够引用到任何别的对象一样。不要忘记JVM根本就不知道你正在运行groovy代码,因此你能处理闭包对象也没有什么特别奇怪,闭包仅仅是一个对象,groovy提供了一种非常容易创建闭包的方式和启用了一些非常常用的行为。
处理资源上,JAVA需要try,catch,finally,在finally里close掉这个资源,而在groovy中,可以像以下代码:
new File('myfile.txt').eachLine { println it }
file的eachLine方法负责处理文件输入流的打开和关闭,这样避免我们偶尔的错误处理形成资源泄露。在JDK7中提供了try-with-resource,可以自动关闭相关的资源(只要该资源实现了AutoCloseable接口,jdk7为绝大部分资源对象都实现了这个接口):
static String readFirstLineFromFile(String path) throws IOException{
try(BufferedReader br = new BufferedReader(new FileReader(path))){
return br.readLine();
}
}