Pragmatic.Bookshelf.Programming.Groovy 笔记摘选1-12章

编程实践之一

建立不同的source folder,分别保存java和Groovy,也可以再建立专门的test和config包

main函数的参数可以不提供类型

使用def定义对象,Groovy会自动的尽可能的推算出类型,缺少类型能带来的好处,省去了接口和抽象类
在方法声明参数,以及for循环中,都不需要def关键字,或者声明类型

方法体的本质就是一个for循环 所以参数不需要声明类型

集合的*. 调用会调用集合的每一个对象

对象的?.方法 可以预防调用null对象

可以直接使用Junit4进行测试
@Test放置在方法之上

如果在定义Groovy风格的JavaBean的时候,如果不定义属性的类型,那么set和get得到的都是Object

0.upto(2) { print "$it "} 方法等同与 0..2
0.step(10, 2) { print "$it "} 表示0到10 之间 +2一次

"svn help".execute().text

"groovy -v".execute().text 直接调用本地程序的方式

"cmd /C dir".execute().text

异常的监听方法和其他的一样
使用try..catch(ex){} 可以省略异常类型

定义一个对象为final 可以让他在JavaBean中为只读的

str.class.name 得到类名 等同于 str.getClass().getName()

便捷的事件处理
button.addActionListener(
{ JOptionPane.showMessageDialog(frame, "You clicked!") } as ActionListener
)

使用闭包Closures的方式添加全局的事件监听函数,注意使用 as 关键字进行类型转换
displayMouseLocation = { positionLabel.setText("$it.x, $it.y") }
frame.addMouseListener(displayMouseLocation as MouseListener)

同时添加多个监听的方法
handleFocus = [
focusGained : { msgLabel.setText("Good to see you!") },
focusLost : { msgLabel.setText("Come back soon!") }
]
button.addFocusListener(handleFocus as FocusListener)
这里添加的是同个组件类型的事件监听

添加多个不同类型监听的方法

events = ['WindowListener', 'ComponentListener']

handler = { msgLabel.setText("$it") }
for (event in events)
{
handlerImpl = handler.asType(Class.forName("java.awt.event.${event}"))
frame."add${event}"(handlerImpl)
}
这里注意${},asType(),以及Class.forName反射的使用

Groovy中布尔值的判断,区别与Ruby中的nil以及false

1:如果一个字符串非空,那么在条件语句里面 他就是true

2:如果集合里面为empty,那么也表示false,根据长度来进行判断

3:大多的类型,都是根据是否有内容来判断,返回boolean值

循环的格式
for(i in 'a'..'c')

对于Java 1.5 新特性的支持
Java下数组的定义使用{}包围元素, Groovy下使用[]定义List

使用for-each循环的时候
java下为 for(Type arg : args){}
Groovy下为支持上面的类型的同时 可以变形为
for(arg:args) 以及 for(arg in args)

Enum 枚举的使用
定义一个枚举
enum CofferSize{SHORT,SMALL}

循环枚举属性的时候
for(size in CofferSize.values){...}

可以在定义枚举的时候,设置默认值,以及定义方法,供每个枚举属性使用

也可以定义枚举的构造函数(类似的东西),然后在枚举对象调用方法的时候,会进行调用 P62

支持Java 5的不定数类型参数
def fool(int a,int... b)

def foo2(int a, int[] b)

静态导入 static import

静态导入可以直接导入static方法,也可以重命名(alias)类,方便引用

在Groovy中,如果方法的最后一行不是一个合理的表达式,比如{ ,那么该方法将返回null

字符串中的${}同样也可以调用方法

Groovy中==进行的是值的比较

两个不同类型对象使用 == 进行比较的时候,会调用equals方法,
如果是同类型的,就会调用compareTo方法,需要实现Comparable方法

使用Java命令查看指定类型的字节码
javap -c ClassFileName

Groovy不支持内部类

判断helper对象是否有eatSugarcane方法
helper.metaClass.respondsTo(helper, 'eatSugarcane')

使用def定义方法的话 那默认返回一个Object, 在使用Junit测试的时候需要注意返回的是void,写测试用例需要注意

DSLs
5.days.ago.at 4:30

Java中的集合 使用remove后,集合的父类对象无法remove掉子类中的对象,但是可以指向同一个引用

闭包 closures
闭包的用处之一 就是可以将其做为参数传递,自由传递的方法
声明一个匿名的闭包只要使用{} 就可以 ,使用it作为默认的参数,多个参数使用{a,b ->...} 进行命名

h.testClosure(10){println it}; 调用的方法 等同于 h.testClosure(10,{println it});

Groovy定义的方法,可以不写在类的里面,就是可以不声明class,不过需要包名...也可以使用和其他类一样的方法来定义

方法名与closure名字要一样,而且闭包的参数需要2个来接收

定义方法后需要显示的调用,另外种角度来说,是后面的closure调用了前面def的方法

taxComputer.maximumNumberOfParameters 其中第一个为传入的closure对象,后面的参数为获取该closure需要的参数

closure.parameterTypes 获取closure的参数类型

使用closure() 可以调用闭包方法

String的使用
Groovy中声明一个String 使用'' 或者""都可以
如果使用''单引号声明一个字符串,那么其中的表达式将被过滤掉, 比如${}

String是不可改变的,可以使用[]方法字符串指定的位置,可是不可以用来修改

在""字符串中, 使用\符号,进行转译掉特殊符号

字符串中的${} 会自动和参数进行绑定 bingable

GString 指带${}或者//的字符串

可以在字符串中绑定closure,使用${closure名}的方式进行数据的绑定,由closure进行中转的绑定
方法一: companyClosure={it.write(company)} 这里尤其注意使用write方法,表示返回值

方法二: companyClosure={-> company} 表示返回值

方法三: 直接在使用的字符串中"${-> company}" 进行使用

创建多行的文本 使用"""...""" 或者'''...'''均可

可以很便捷的生成需要的xml list.each...p119

String中运算符重载,可以使用-= 去掉字符串中指定的字符
同样也可以使用'abc..abe' 方式进行递增的循环

可以使用pattern= ~ "cc" 的方式声明一个正则表达式,注意中间的空格,或者使用/regex/
也可以使用它进行测试是否符合知道的正则

=~返回一个metch对象 ==~返回是否匹配

快速的替换内容 result = (str =~ /groovy/).replaceAll('hip')

Collection的使用

each{}方法是比较常用的循环一个List

collect{}方法会循环集合,然后返回一个新的集合 区别与each,结果是否返回

find用于搜索指定的内容 lst.find{it==2},找到则返回该对象,否则返回null
扩展的方式有findall{}

注意以上方法都是closure风格的{}调用

lst.collect { it.size() }.sum() 指定 的list集合中所有字符串的长度

lst.inject(0) { carryOver, element -> carryOver + element.size() } 方法二 作用同上

join(' ')方法用于合并集合

lst.flatten() 拆开List中的[]子对象
也可以使用 - 运算符 从List减去一个对象

reverse( ) 得到一个原列表的拷贝,其中的顺序是逆向的

list*.size() 方便的对集合的每个元素进行操作,并且返回一个结果集合

传入一个集合给方法的时候,可以用多个参数分别进行接受
def words(a, b, c, d)
{
println "$a $b $c $d"
}
words(*lst)

map键值对形式的集合 使用map['key'] 进行元素访问 , 也可以直接少用map.key来访问,其中需要注意特殊字符为key存在的问题
那时候就要使用[key]方式来访问,也可以使用map.'c++' 进行访问

循环map

map.each{entity -> "$entity.key" + entity.value} 注意使用key和value访问
同时map的each方法也可以提供两个参数,只要修改{}内接受参数的数量即可

map还有any和where方法 都传入两个参数 取1和取多的区别

str.dump() 方法,取到该对象的详细信息

identity{} 模拟一个闭包函数 其中有三个对象 this owner delegate对象

new Object().sleep(2000) 线程休眠2000毫秒

使用each{name -> } 重命名进行动态的添加属性,以及修改
,用于加载不同的属性,只要保证key名字相同 p146

动态的调用方法 使用字符串中获取方法调用
peter = new Person()
peter.invokeMethod("walk", null) 其中walk为方法名,第二个为参数
peter.invokeMethod("walk", [2, 'uphill'] as Object[]) 第二个为参数,方法中使用两个分别的参数进行接收
一个参数的时候,直接使用一个参数进行接受

next( ) for operator ++

Thread.start{}...开始一个新线程
Thread.startDaemon{} 开始一个守护线程

线程中的休眠
sleep(3000){....} 中间应该是休眠后触发的内容,守护线程不会触发sleep后的内容

new Object().sleep 会导致主线程休眠

io处理
File('thoreau.txt').text 快速的读取一个文件中的所有内容

withStream{} 提供了自动关闭和flush刷新流的方法
withWriter{} 也提供了类似的功能

Working with XML

GPath解析 类似E4x的方式解析xml

DOMCategory方式进行解析
加载的过程
先加载一个Dom元素
document = groovy.xml.DOMBuilder.parse(new FileReader('languages.xml'))

rootElement = document.documentElement

use(groovy.xml.dom.DOMCategory)
{
println "Languages and authors"
languages = rootElement.language
languages.each { language ->
println "${language.'@name'} authored by ${language.author[0].text()}"
}
def languagesByAuthor = { authorName ->
languages.findAll { it.author[0].text() == authorName }.collect {
it.'@name' }.join(', ')
}
println "Languages by Wirth:" + languagesByAuthor('Wirth')
}

使用DOMCategory时候,必须把代码放置在use块中

XMLParser的方式进行解析
也需要进行加载xml
languages = new XmlParser().parse('languages.xml')

println "Languages and authors"
languages.each {
println "${it.@name} authored by ${it.author[0].text()}"
}
def languagesByAuthor = { authorName ->
languages.findAll { it.author[0].text() == authorName }.collect {
it.@name }.join(', ')
}
println "Languages by Wirth:" + languagesByAuthor('Wirth')


最大的区别是不需要放置在use块里面,上述两种都是将整个文件导入到内存

XMLSlurper 用于大型的xml解析

languages = new XmlSlurper().parse('languages.xml')

println "Languages and authors"
languages.language.each {
println "${it.@name} authored by ${it.author[0].text()}"
}
def languagesByAuthor = { authorName ->
languages.language.findAll { it.author[0].text() == authorName }.collect {
it.@name }.join(', ')
}
println "Languages by Wirth:" + languagesByAuthor('Wirth')

在xml里面定义命名空间,用于区别名字相同的元素
<languages xmlns:computer="Computer" xmlns:natural="Natural">
<computer:language name="Java"/>
<natural:language name="French"/>
</languages>
两个不同命名的开头: 调用方式
languages = new XmlSlurper().parse(
'computerAndNaturalLanguages.xml').declareNamespace(human: 'Natural')

print "Languages: "
println languages.language.collect { it.@name }.join(', ')

print "Natural languages: "
println languages.'human:language'.collect { it.@name }.join(', ')

用于不同的读取方式,注意大的xml文档,需要使用XMLSlurper,为了节约内存开销

创建XML Creating XML

一般使用多行""" """ 字符串的格式,配合${}循环一个map生成字符串
,最后再加上一个roots根节点标签


另外中特殊的xml生成 带nameSpace的
langs = ['C++' : 'Stroustrup', 'Java' : 'Gosling', 'Lisp' : 'McCarthy']

xmlDocument = new groovy.xml.StreamingMarkupBuilder().bind {
mkp.xmlDeclaration()
mkp.declareNamespace(computer: "Computer") //设置命名空间 <computer:language name='C++'>
languages {
comment << "Created using StreamingMarkupBuilder" //添加注释
langs.each { key, value -> //添加节点
computer.language(name: key) {
author (value)
}
}
}
}
println xmlDocument


与数据库配合使用 Working with Databases;

连接数据库的方法

def sql = groovy.sql.Sql.newInstance('jdbc:mysql://localhost:3306/weatherinfo',
userid, password, 'com.mysql.jdbc.Driver')

println sql.connection.catalog
输出数据库的名称

查询,并且遍历查询结果
sql.eachRow('SELECT * from weather') {
printf "%-20s%s\n", it.city, it[1]
}
其中使用两种方法指定显示的列 it.列名 或者it[1] 1为列索引


获取列名
processMeta = { metaData ->
metaData.columnCount.times { i ->
printf "%-21s", metaData.getColumnLabel(i+1)
}
需要附加在查询语句之上
sql.eachRow('SELECT * from weather', processMeta) {
printf "%-20s %s\n", it.city, it[1]
}
位于eachRow内的第二个参数

获取指定SQL的查询结果, 会自动封装在一个ArrayList中
rows = sql.rows('SELECT * from weather')

查询第一行结果
firstRow( )

将查询结果转换成XML

bldr = new groovy.xml.MarkupBuilder()
bldr.weather {
sql.eachRow('SELECT * from weather') {
city(name: it.city, temperature: it.temperature)
}
}
将会生成如下格式
<weather>
<city name="" temperature=""/>
</weather>

使用DataSet
提供一个查询代理,并不会直接查询出所有的结果,并且可以提供数据过滤

dataSet = sql.dataSet('weather')

citiesBelowFreezing = dataSet.findAll { it.temperature < 32 }

citiesBelowFreezing.each {
println it.city
}
其中sql为已经初始化好的Sql对象

插入和更新
dataSet.add(city: 'Denver', temperature: 19)

SQL的形式插入数据
sql.executeInsert("""INSERT INTO weather (city, temperature)
VALUES ('Oklahoma City', ${temperature})""")

访问Excel文档 扩展名为xlsx 或者xls

def sql = groovy.sql.Sql.newInstance(
"""jdbc:odbc:Driver=
{Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};
DBQ=C:/temp/weather.xlsx;READONLY=false""", '', '')

sql.eachRow('SELECT * FROM [temperatures$]') {
println "${it.city}\t\t${it.temperature}"
}


使用脚本和类
Working with Scripts and Classes

JSR-223 用于调用Groovy Script

Groovy中调用Groovy的脚本 使用如下
shell = new GroovyShell(binding) //其中的参数是在目标需要参数的情况下指定的
result = shell.evaluate(new File('Script1a.groovy')) //()中的为类名
注意其中要声明个和被调用脚本相同的名字的参数 P178
result为调用类的返回结果,即最后一行的声明
并且会修改掉绑定需要的参数

另外种,使用Binding对象的声明方式

binding1 = new Binding()
binding1.setProperty('name', 'Venkat')
shell = new GroovyShell(binding1)
shell.evaluate(new File('Script1a.groovy'))

如果需要传递一些方法参数,可以用run()代替evaluate

JSR223 目前只支持Groovy1.0
暂且略过


MOPping Groovy (Meta-Object Protocol) 元对象协议

Plain Old Groovy Objects (POGOs) ,该类型对象实现了GroovyObject接口
提供了runtime改变对象的类的方法setMetaClass( )

GroovyInterceptable 标记型接口,提供了拦截方法的能力

getMetaMethod( ) 方法
getStaticMetaMethod( ) 静态方法
getMetaProperty( ) 属性

动态调用方法
其中methodName为方法名
methodOfInterest = str.metaClass.getMetaMethod(methodName)
println methodOfInterest.invoke(str)

String.metaClass.respondsTo(str, 'compareTo', "test") 调用带参数的方法

调用方法和访问属性的其他方式
obj[usrRequestedProperty]
obj."$usrRequestedProperty"
obj."$usrRequestedMethod"()
obj.invokeMethod(usrRequestedMethod, null)
1,2为属性 3,4为方法

'hello'.properties.each { println it } 打印出指定对象的属性..properties
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值