1 Groovy的诞生
Groovy是一门几经重生的语言,该语言由James Stracham和Bob McWhirter于2003年启动开发,之后于2004年3月成为JSR241(Java Specification Request,即Java规范请求)。不久因为存在一些困难和问题几近放弃。Guillaume Laforge和Jeremy Rayner决定再次努力,首先修复了一些bug,同时使语言稳定下来,后来他们又邀请了一些有丰富经验的开发者加入他们的团队,最终一个形成了一个充满生气的开发者社区,并使Groovy重获新生。
Groovy的第一个版本于2007年1月发布,目前最新版本是4.0.5,在这之前很多组织已经将Groovy应用于商业项目,大量使用Groovy语言的时机已经成熟。
2 为何选择Groovy
Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
Groovy是JVM的一个替代语言(Groovy 可以在Java平台上进行 Java 编程),使用方式基本与使用 Java代码的方式相同,该语言特别适合与Spring的动态语言支持一起使用,设计时充分考虑了Java集成,这使 Groovy 与 Java 代码的互操作很容易。(注意:不是指Groovy替代java,而是指Groovy和java很好的结合编程。
作为Java程序员我们不必要切换到一门自己完全不熟悉的语言上,使用Groovy感觉就好像对已经熟知的Java作了一些扩展。
当前很多脚本语言都可以在JVM上运行,除了Groovy外,还有JRuby、JPython、BeanShell和JavaScript等。基于下面的一些特点让我们选择了Groovy:
- 易于掌握
- 遵循Java语义
- 动态语言
- 扩展了JDK
如果你是一个Java程序员,你可以把Java代码当作Groovy代码来运行,在你学习了Groovy之后,把它修改为Groovy风格的代码,这意味着学习越来非常容易。例如,在Java中我们写一个for循环:
for(int i=0;i<10;i++){
...
}
用Groovy改写如下:
10.times{
...
}
在使用Groovy编程时,Java有的Groovy几乎都有。Groovy同样扩展了java.lang.Object类,Groovy类就是Java类,Java语义都保留下来了,所以使用Groovy编写的表达式和语句,对于Java程序员而言,理解它没有任何障碍。
选择Groovy的另一个原因是它是动态语言。同为动态语言还包括Smalltalk、Python、Ruby、JavaScript等。所谓动态语言是指在运行时扩展程序,包括修改类型、行为、对象结构。静态语言在编译时做的事情,动态语言可以在运行时做。作为开发者通过使用动态语言,可以让我们的工作更有效率。更高的效率意味着可以快速创建一些应用,从而得到测试人员、项目经理、客户代表的反馈,而这一切以会使我们更加敏捷。
实时效果反馈
1. 选择Groovy的原因不包括下面哪个?
A 易于掌握
B 动态语言
C 只能在JVM上运行
D 运行速度快
答案
1=>C
3 在Windows下安装Groovy
进入https://groovy.apache.org/download.html
点击Download4.0.5
按钮,开始下载,下载完成后解压apache-groovy-sdk-4.0.5.zip
文件。
配置环境变量
打开Window下环境变量设置窗口,新建一个名为GROOVY_HOME
的环境变量,变量的值是解压后Groovy sdk的目录,我们可以把它放在c:\program files\
目录下,如下图:
选中Path变量进行编辑
在Path变量中添加%GROOVY_HOME%\bin
完成后点击“确定”。
打开window的命令提示窗口,输入
C:\Users\a>groovy -v
如果出现以下版本信息,则安装成功。
C:\Users\a>groovy -v
Groovy Version: 4.0.5 JVM: 1.8.0_291 Vendor: Oracle Corporation OS: Windows 10
4 使用Groovy控制台运行
安装完成后,在bin目录下有一个GroovyConsole.bat
文件,双击打开,可以在里面编写并执行Groovy程序。
Window系统用户按Ctrl+Enter或Ctrl+R执行代码。
5 在IntelliJ Idea中运行
在File菜单下点击new project...
,选择Groovy
类型的项目,并设置好Groovy library
,点击
Next
填写项目名称及路径,完成后点击Finish
。
点击Tools菜单下的Groovy Console...
,开始编写程序
完成后,点击红色小方块中的运行按钮执行程序,运行结果显示在下方的控制台区域。
6 Groovy与Java的关系
我们先从一个Java代码编写的例子开始,同时它也是Groovy代码,保存在一个后缀为groovy的文件中。
//Java代码
public class GrvTest01 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("ho");
}
System.out.println("Hello Groovy");
}
}
在window命令提示窗口中使用groovy命令执行这段程序
可以看到,Java编写的代码在groovy中可以正常运行,实际只保留for循环和输出语句,其它的都可以不要,依然可以正常运行。例如:
for (int i = 0; i < 10; i++) {
System.out.println("ho");
}
System.out.println("Hello Groovy");
把它保存为GrvTest02.groovy,执行groovy运行
可以看到运行结果与前面的完全一样,但代码要简洁得多。甚至可以更进一步,可以简化成下面的代码,效果也是一样的
for(i in 1..10){println 'ho'}
println 'Hello Groovy'
我们把它保存为GrvTest03.groovy,然后在命令行执行
结果和前面一样,而且代码更轻便了
GDK介绍
Groovy虽然支持Java的语法但它并没有强迫我们学习新的类和库,而是通过向JDK中各种类添加方法,所以说Groovy扩展了JDK,这些扩展称之为GDK(Groovy JDK)。
Java中可以使用java.lang.process与系统级进程进行交互,例如,我们在代码中调用git的help命令并把help的内容打印出来,用Java的实现代码如下:
public class ExceuteProcess {
public static void main(String[] args) {
try {
Process process = Runtime.getRuntime().exec("git help");
BufferedReader result = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line = result.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果如下图:
Groovy通过在java.lang.String类上添加一个execute()方法,使这一切变得很简单。
println "git help".execute().text
运行后输出的结果与上面的一模一样。
通过此例,我们看到GDK的扩展功能使程序员的编码工作更为轻松,但这仅仅是GDK的一点皮毛。
实时效果反馈
1. 关于Groovy描述不正确的是?
A 同时运行静态和动态类型
B Groovy和Java很相似
C Groovy是轻量级的Java,且更为易用
D Groovy直接使用JDK,没有进行任何扩展
答案
1=>D
7 Groovy的数据类型
7.1 Groovy的保留字
abstract | assert | break | case |
---|---|---|---|
catch | class | const | continue |
def | default | do | else |
enum | extends | final | finally |
for | goto | if | implements |
import | instanceof | interface | native |
new | null | non-sealed | package |
public | protected | private | return |
static | strictfp | super | switch |
synchronized | this | threadsafe | throw |
throws | transient | try | while |
一般来说这些保留字不能用于定义变量、方法。如果用双引号引起来,也可以作为方法名,但不推荐这么做。例如:
def "abstract"() { true }
7.2 标识符
Groovy标识符以字母、美元符号或下划线开头,不能以数字开头。下面是合法的标识符:
def name
def item3
def with_underscore
def $dollarStart
下面是非法的标识符
def 3tier
def a+b
def a#b
1. 下面Groovy的标识符定义正确的是?
A #ab
B -m_user
C 3a_b
D $ab
答案
1=>D
7.3 字符串
Groovy允许实例化java.lang.String
类来定义一个字符串对象,同样地,也可以通过实例化groovy.lang.GString
类定义一个字符串对象,两者可以混合使用。
例如,用单引号引起来的字符串
'a single-quoted string'
三个单引号引起来的字符串
'''a triple-single-quoted string'''
def aMultilineString = '''line one
line two
line three''' //'''定义的字符串可以折行,无需连接或转义字符
说明:用单引号或三个单引号定义的字符串不支持混合编程
双引号引起来的字符串
"a double-quoted string"
在Groovy中使用${}作为占位符,占位符里面代表一个表达式的值,例如:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume'
三个双引号引起来的字符串
用"""引号引起来的字符串行为上和双引号引起来的字符串是一样的,不同之处在于它代表多行字符串,就像三个单引号定义的字符串。例如:
def name = 'Groovy'
def template = """
Dear Mr ${name},
You're the winner of the lottery!
Yours sincerly,
Dave
"""
Groovy字符串总结
字符串名称 | 语法 | 是否可以混用 | 是否多行 | 转义字符 |
---|---|---|---|---|
单引号的 | '…' | \ | ||
三个单引号的 | '''…''' | √ | \ | |
双引号的 | "…" | √ | \ | |
三个双引号的 | """…""" | √ | √ | \ |
斜线的 | /…/ | √ | √ | \ |
1. 关于Groovy的字符串说法不正确的是?
A groovy字符串种类比Java字符串更多
B 所有groovy字符串种类都可以与Java混合使用
C groovy字符串都是有序序列,可以通过下标访问单个字符
D 三引号定义的字符串用于定义多行字符串变量
答案
1=>B
7.4 数值类型
Groovy的数值型包括整数型(Integer)和小数型(decimal)两种,整型又包括以下几种:
- byte
- char
- short
- int
- long
- java.math.BigInteger
整数型
//基本类型
byte b = 1
char c = 2
short s = 3
int i = 4
long l = 5
//无限精度型
BigInteger bi = 6
使用def关键字定义数值型变量
def a = 1 //整型
assert a instanceof Integer
// 定义整型最大值 Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer
// 定义整型最大值+1,Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long
// 定义long型最大值 Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long
// 定义long型最大值+1,Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigInteger
定义二进制数,以0b
为前缀
int xInt = 0b10101111
assert xInt == 175 //整型
short xShort = 0b11001001
assert xShort == 201 //短整型
byte xByte = 0b11
assert xByte == 3 //字节型
long xLong = 0b101101101101
assert xLong == 2925l //long型
BigInteger xBigInteger = 0b111100100001
assert xBigInteger == 3873g //BigInteger型
int xNegativeInt = -0b10101111
assert xNegativeInt == -175 //负整数
定义八进制数,以0
为前缀
int xInt = 077
assert xInt == 63
short xShort = 011
assert xShort == 9
byte xByte = 032
assert xByte == 26
long xLong = 0246
assert xLong == 166l
BigInteger xBigInteger = 01111
assert xBigInteger == 585g
int xNegativeInt = -077
assert xNegativeInt == -63
定义16进制数,以0x
为前缀
int xInt = 0x77
assert xInt == 119
short xShort = 0xaa
assert xShort == 170
byte xByte = 0x3a
assert xByte == 58
long xLong = 0xffff
assert xLong == 65535l
BigInteger xBigInteger = 0xaaaa
assert xBigInteger == 43690g
int xNegativeInt = -0x77
assert xNegativeInt == -119
小数型(decimal)
以下定义的变量都属于小数型
// 基本类型
float f = 1.234
double d = 2.345
// 带有精度的小数类型
BigDecimal bd = 3.456
可以使用科学计数法表示相应类型的数值,例如:
assert 1e3 == 1_000.0
assert 2E4 == 20_000.0
assert 3e+1 == 30.0
assert 4E-2 == 0.04
assert 5e-1 == 0.5
Groovy支持用下划线对数字进行分割,使得数字更容易识别
long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010
各种类型数值的后缀
类型 | 后缀 |
---|---|
BigInteger | G or g |
Long | L or l |
Integer | I or i |
BigDecimal | G or g |
Double | D or d |
Float | F or f |
例如:
assert 42I == Integer.valueOf('42')
assert 42i == Integer.valueOf('42') // 小写i可读性更好
assert 123L == Long.valueOf("123") // 大写L可读性更好
assert 2147483648 == Long.valueOf('2147483648') // Long type used, value too large for an Integer
assert 456G == new BigInteger('456')
assert 456g == new BigInteger('456')
assert 123.45 == new BigDecimal('123.45') // default BigDecimal type used
assert .321 == new BigDecimal('.321')
assert 1.200065D == Double.valueOf('1.200065')
assert 1.234F == Float.valueOf('1.234')
assert 1.23E23D == Double.valueOf('1.23E23')
assert 0b1111L.class == Long // binary
assert 0xFFi.class == Integer // hexadecimal
assert 034G.class == BigInteger // octal
不同类型数值进行算术运算的规则
对于二元运算符,两个不同类型的数值进行运算后它们的结果按照以下规则确定
- 对于byte、char、short、int这几种类型之间运算的结果为int
- 涉及long与byte、char、short、int之间运算的结果为long
- 涉及BigInteger与其它类型数值之间的运算结果为BigInteger
- BigDecimal与byte、char、short、int之间的运算结果为BigDecimal
- float、double与BigDecimal之间的运算结果为double
- 两个BigDecimal之间的运算结果为BigDecimal
1. 关于Groovy的数值型说法不正确的是?
A 数值型包括整数型和小数型
B 二进制、八进制、16进制可以用于表示小数类型
C 小数型包括float、double、BigDecimal三种类型
D 整数型包括byte、char、short、int、long和Java.math.BigInteger类型
答案
1=>B
7.5 布尔型
包含true、false两个基本布尔值,下面是定义布尔型变量的例子:
def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true
7.6 集合类型
Groovy没有自己的集合类型,它的List类型实际上用的就是JDK中的java.util.List包。当我们定义一个集合对象,Groovy默认采用Java.util.ArrayList类型。
Groovy使用,
把集合中的元素分隔开,外面用[]
进行包裹。下面是定义集合对象的例子:
def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3
也可以在集合中放置不同类型的元素,例如:
def heterogeneous = [1, "a", true]
默认定义的集合对象属于Java.util.ArrayList类,也可以用as运算符,强制定义List接口的其它实现类的对象,例如:
def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList
def linkedList = [2, 3, 4] as LinkedList
assert linkedList instanceof java.util.LinkedList
LinkedList otherLinked = [3, 4, 5]
assert otherLinked instanceof java.util.LinkedList
访问List集合中的元素使用[]
下标运算符,其中的数值可以是正值也只可以是负值,例如:
def letters = ['a', 'b', 'c', 'd']
assert letters[0] == 'a'
assert letters[1] == 'b'
assert letters[-1] == 'd'
assert letters[-2] == 'c'
letters << 'e' //使用左移运算符,在集合末尾添加元素
assert letters[4] == 'e'
assert letters[-1] == 'e'
Groovy可以定义多维集合,例如:
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2 //第1个集合中的第0个元素
1. 关于Groovy的集合类型说法不正确的是?
A Groovy集合类对象中的元素类型必须相同
B Groovy集合类型默认使用Java.util.List中的类
C 可以像访问数组元素那样访问集合中的元素
D 可以使用as运算符强制转为其它List接口的实现类
答案
1=>A
7.7 数组
Groovy中数组和集合的表示方式相同,也就是说Groovy复用list的表示形式来表示数组,但必须显式的声明数组的类型,例如:
String[] arrStr = ['Ananas', 'Banana', 'Kiwi']
assert arrStr instanceof String[] //断言为true
assert !(arrStr instanceof List) //断言为true
使用as运算符,强制转为要定义的类型
def numArr = [1, 2, 3] as int[]
assert numArr instanceof int[]
也可以定义多维数组
def matrix3 = new Integer[2][3] //定义一个二维数组,并指定元素个数
assert matrix3.size() == 2 //维数为2
Integer[][] matrix2 //声明时不指定数组元素个数
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]
访问数组元素时按照和list一样的方式,使用下标运算符[]
,例如:
String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric' //访问第0个元素
names[2] = 'Blackdrag' //给第二个元素重新赋值
assert names[2] == 'Blackdrag'
Java风格数组初始化
//java风格数组初始化方式
def primes = new int[] {2, 3, 5, 7, 11}
assert primes.size() == 5 && primes.sum() == 28
def pets = new String[] {'cat', 'dog'}
assert pets.size() == 2 && pets.sum() == 'catdog'
//Groovy定义初始化数组的方式
String[] groovyBooks = [ 'Groovy in Action', 'Making Java Groovy' ]
assert groovyBooks.every{ it.contains('Groovy') }
1. 关于Groovy的数组类型说法不正确的是?
A Groovy数组对象的定义和集合对象的定义形式相同
B Groovy数组采用下标运算符[]
访问元素或给元素赋值
C Groovy也支持采用大括号的形式初始化数组
D 数组里所有元素的类型可以不相同
答案
1=>D
7.8 map类型
Groovy使用中括号定义一个map,map中的key/value对用逗号分割,例如:
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
assert colors['red'] == '#FF0000'
assert colors.green == '#00FF00'
colors['pink'] = '#FF00FF' //使用[]增加一个新的key/value
colors.yellow = '#FFFF00' //也可使用.新增一个key/value
说明:
Groovy定义的map对象实际上是Java.util.LinkedHashMap类的实例
key值不光可以用string也可以用其它数据类型,比如,整型
def numbers = [1: 'one', 2: 'two']
下面的例子中用key这个变量定义了一个key,它的值为name,但Groovy会认为,map中的key是变量本身而不是赋于它的值。
def key = 'name'
def person = [key: 'Guillaume']
assert !person.containsKey('name')
assert person.containsKey('key')
如果要给map里传变量名,必须使用这种形式(变量名)
,例如:
person = [(key): 'Guillaume']
assert person.containsKey('name')
assert !person.containsKey('key')
1. 关于Groovy的map类型说法不正确的是?
A map使用的是Java.util.LinkedHashMap
B map m1 = [‘name’:‘Jack’,‘age’:18,‘sex’:‘male’]这是一个正确的map定义
C map中的key值不仅仅是String类型也可以是其它类型
D 可以通过下标运算符[]
来访问或新增map中的元素
答案
1=>B
8 Groovy的运算符
8.1 算术运算符
运算符 | 作用 | 备注 |
---|---|---|
+ | 加 | 同时也是一元运算符,如:+4 |
- | 减 | 同时也是一元运算符,如:-2 |
* | 乘 | |
/ | 除 | intdiv() 专门用于整数相除 |
% | 取余 | |
** | 次幂 | 例如:2 ** 3 =8 |
赋值运算符
+=
-=
*=
/=
%=
**=
def a = 4
a += 3 //等价于a=a+3
assert a == 7
def b = 5
b -= 3 //等价于b=b-3
assert b == 2
def c = 5
c *= 3 //等价于c=c*3
assert c == 15
def d = 10
d /= 2 //等价于d=d/2
assert d == 5
def e = 10
e %= 3 //等价于e=e%3
assert e == 1
def f = 3
f **= 2 //等价于f=f**2
assert f == 9
1. 下列表达式结果错误的是?
A 2++*3的结果等于6
B 2–*3的结果等于6
C ++2+3的结果等于6
D --1-1的结果等于0
答案
1=>D
8.2 关系运算符
运算符 | 作用 |
---|---|
== | 相等 |
!= | 不等 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
=== | 完全等于 (Since Groovy 3.0.0) |
!== | 不完全等于 (Since Groovy 3.0.0) |
assert 1 + 2 == 3
assert 3 != 4
assert -2 < 3
assert 2 <= 2
assert 3 <= 4
assert 5 > 1
assert 5 >= -2
下面是groovy判断===
(全等)和!==
(不全等)运算符的示例:
import groovy.transform.EqualsAndHashCode
@EqualsAndHashCode
class Creature { String type }
def cat = new Creature(type: 'cat')
def copyCat = cat
def lion = new Creature(type: 'cat')
assert cat.is(copyCat) // Groovy的is()方法判断两个对象是否相等
assert cat === copyCat // ===是is()的简洁操作符
assert cat !== lion // negated operator shorthand
8.3 逻辑运算符
Groovy提供了三种逻辑运算符用于布尔表达式,分别是:
&&
:逻辑与||
:逻辑或!
:逻辑非
例如:
assert true && true
assert true || false
assert !false
注:逻辑非运算符!
优先级高于逻辑与&&
,逻辑与运算符&&
优先级高于逻辑或||
,例如:
assert (!false && false) == false
assert true || true && false
短路运算规则
逻辑与&&
和逻辑或||
都支持短路运算,对于||
只要左边的操作数为true,不需要再判断右边的操作数就知道整个表达式的结果为true。对于&&
只要左边的操作数为false,不需要再判断右边的操作数就知道整个表达式的结果为false。
1. 下列逻辑运算符优先级正确的是?
A !>&&>||
B &&>||>!
C ||>!>&&
D &&>!>||
答案
1=>A
8.4 位运算符和位移运算符
Groovy的位运算符如下:
&
: 按位与|
: 按位或^
: 按位异或~
: 按位取反
位运算符的操作数为整型数值,包括byte、short、int、long、BigInteger。如果操作数的类型是BigInteger那么返回结果的类型也是BigInteger;如果操作数是int型,返回结果也是int型;如果操作数是long型,返回结果也是long型。
Groovy提供了三种位移运算符,分别是:
<<
: 左移运算符>>
: 右移运算符>>>
: 右移无符号运算符
assert 12.equals(3 << 2)
assert 24L.equals(3L << 3)
assert 48G.equals(3G << 4)
1. 下列关于位运算和位移运算规则说法正确的是?
A 位运算中一个参数的类型为BigInteger,那么运算结果的类型也是BigInteger
B 位运算中两个参数类型分别为Long和BigInteger,那么运算结果的类型是Long
C 位运算中两个参数类型分别为Long和int,那么运算结果的类型是int
D 位移运算中的左侧操作数的类型只能是byte、short、int、long、BigInteger
答案
1=>A
8.5 条件运算符
条件运算符可用于取代if-else判断,例如:
if (string!=null && string.length()>0) {
result = 'Found'
} else {
result = 'Not found'
}
利用条件运算符可以写成如下形式:
result = (string!=null && string.length()>0) ? 'Found' : 'Not found'
从Groovy3.0开始,引入了elvis操作符,也就是条件运算符的简便形式,例如:
displayName = user.name ? user.name : 'Anonymous' //正常形式
displayName = user.name ?: 'Anonymous' //elvis操作符
8.6 对象运算符
安全导航运算符(safe navigation operator)
主要作用为避免出现NullPointerException
异常,如果出现空指针异常,使用安全导航运算符将返回null,而不是抛出异常。例如:
def person = Person.find { it.id == 345 } //调用find方法查找
def name = person?.name //如果查找的对象不存在,返回null
assert name == null
直接字段访问运算符(Direct field access operator)
使用该运算符可以不用调用get方法而直接获取字段的值,例如:
class User {
public final String name
User(String name) { this.name = name}
String getName() { "Name: $name" }
}
def user = new User('Bob')
assert user.name == 'Name: Bob'
assert user.@name == 'Bob' //使用.@字段名代替getter方法
方法引用运算符(method reference operator)
方法引用运算符形如两个冒号::
,目的就是调用方法。例如:
assert 6G == [1G, 2G, 3G].stream().reduce(0G, BigInteger::add)
调用BigInteger实例的add方法,给每个元素减0,结果仍然不变。
assert [4G, 5G, 6G] == [1G, 2G, 3G].stream().map(3G::add).collect(toList())
3G对象属于BigInteger类型,调用它的add方法,给每个元素加3。
assert [1, 2, 3] == ['1', '2', '3'].stream().map(Integer::new).collect(toList())
def result = [1, 2, 3].stream().toArray(Integer[]::new)
第1行代码使用方法引用运算符调用Integer类的构造方法创建Integer类对象;第2行代码同样地,调用数组类的构造方法创建一个数组对象。
1. 下列关于对象运算符说法错误的是?
A 对象运算符包括null-safe、直接字段访问运算符和方法引用运算符
B nll-safe也称方法安全导航,目的是防止程序抛出NullPointerException
C 直接字段访问运算符相当于调用了字段的get方法来获取字段值
D 方法引用运算符只能调用静态方法而不能调用类的实例方法
答案
1=>D
8.7 正则表达式运算符
~
规则运算符
~
运算符提供了一种简单的方式来创建一个java.util.regex.Pattern对象,例如:
import java.util.regex.Pattern
def p = ~/foo/
assert p instanceof Pattern
=~
查找运算符
=~
运算符用于创建一个java.util.regex.Matcher对象,例如:
//定义一个字符串
def text = "some text to match"
//使用=~运算符创建一个Matcher对象,匹配文本中的match单词
def m = text =~ /match/
==~
匹配运算符
==~
运算符和=~
运算符功能相似,只是返回结果不同,前者返回布尔值,后者返回Matcher对象。例如:
m = text ==~ /match/
assert m instanceof Boolean
if (m) {
throw new RuntimeException("不是Boolean类")
}
1. 下列关于正则运算符说法错误的是?
A 正则运算符有规则运算符、查找运算符和匹配运算符
B 查找和匹配运算符功能相似,只是返回值类型不同
C 查找运算符返回一个布尔值
D 匹配运算符返回布尔值
答案
1=>C
9 Groovy程序的组成
Groovy提供了两种代码方式,一种是脚本一种是类,首先我们定义一个名为Main.groovy 的类。代码如下:
class Main {
static void main(String... args) {
println 'Groovy world!'
}
}
与上面代码功能相同的脚本如下:
println 'Groovy world!'
脚本与类混合
上面的脚本实由groovy.lang.Script类编译成一个class文件,把脚本代码拷贝到groovy.lang.Script类的run方法中进行执行,实际运行的代码形如下面的内容:
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
def run() {
println 'Groovy world!'
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
执行的步骤如下:
- Main.class继承Script类
- 把脚本的主体内容复制到run方法内
- 然后自动生成main()方法,最后运行run()
方法
可以在脚本中定义方法,例如:
int fib(int n) {
n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89
创建的脚本类在编译后会把脚本中的所有方法装配到run方法中,这些对于用户来说都是透明的。
变量
在脚本中定义变量无需声明变量的类型,例如:
int x = 1
int y = 2
assert x+y == 3
上面的定义与如下代码等同:
x = 1
y = 2
assert x+y == 3
这两者在语义上有一些差别,上面的例子中声明的变量属于局部变量,只在run方法内部可见,而下面的无声明变量定义对于其它方法可见,这对于脚本与其它应用程序共享数据就显得很重要了。
1. 下列关于Groovy脚本与类的说法错误的是?
A 脚本其实也是类,经过编译后缀为.groovy
B groovy是一门脚本语言,使用脚本写法更简洁
C groovy类必须有一个static的main()方法
D groovy同时支持脚本和类,可以在类中调用脚本
答案
1=>A
10 Groovy面向对象
Groovy的类
Groovy类是数据的集合和对该数据进行操作的方法的载体,类的数据和方法用于表示问题域中的一些现实世界对象。Groovy中的类声明了该类定义的对象的状态(数据)和行为。因此,Groovy类描述了该类的实例字段和方法。
下面的示例展示了Groovy类的定义及它的组成:
class Student {
int StudentID; //类的成员变量
String StudentName;
static void main(String[] args) {
Student st = new Student();
st.StudentID = 1; //为成员变量赋值
st.StudentName = "Joe"
}
}
getter和setter方法
在任何编程语言中,总是使用private关键字隐藏实例成员,通过提供getter和setter方法来相应地设置和获取实例变量的值。例如下面的代码:
class Student {
private int StudentID;
private String StudentName;
void setStudentID(int pID) {
StudentID = pID;
}
void setStudentName(String pName) {
StudentName = pName;
}
int getStudentID() {
return this.StudentID;
}
String getStudentName() {
return this.StudentName;
}
static void main(String[] args) {
Student st = new Student();
st.setStudentID(1); //通过setter方法设置属性值
st.setStudentName("Joe");
println(st.getStudentID()); //通过getter方法获取值
println(st.getStudentName());
}
}
内部类
内部类定义在另一个类中,外层类可以访问内部类,内部类也可以使用外层类的成员变量,即使是私有的。其它类不能访问内部类。内部类的示例如下:
class Example {
static void main(String[] args) {
Outer outobj = new Outer(); //定义外层类对象
outobj.name = "Joe"; //设置属性值
outobj.callInnerMethod() //调用外层类方法
}
}
//外层类
class Outer {
String name;
def callInnerMethod() {
new Inner().methodA() //外层类调用内部类的方法
}
//内部类
class Inner {
def methodA() {
println(name);
}
}
}
在上面的例子中我们做了以下工作:
- 创建一个名为Outer的类,它将是我们的外层类。
- 在Outer类中定义名为name的字符串。
- 在我们的外层类中创建一个内部或嵌套类。
- 在内部类中,我们可以访问在Outer类中定义的名称实例成员。
继承
extends是用于继承类的关键字,我们通过一个示例介绍groovy中如何继承其它类
//定义Example类
class Example {
static void main(String[] args) {
Student st = new Student(); //创建Student类的对象
st.StudentID = 1; //给它的属性赋值
st.Marks1 = 10;
st.name = "Joe"; //student类通过继承Person类
//获得name属性,并设置它的值
println(st.name);
}
}
//定义Person类
class Person {
public String name;
public Person() {}
}
//定义Student类并继承Person类
class Student extends Person {
int StudentID
int Marks1;
//类的构造器,调用父类的构造器创建Student对象
public Student() {
super();
}
}
抽象类
抽象类表示通用概念,因此它不能被实例化,但可以被继承。抽象类中的抽象方法只有方法的定义而没有方法的实现,它的实现通过继承它的类来完成,定义抽象类通过关键字abstract来声明,抽象方法也是同样的。下面通过一个示例展示定义和使用抽象类:
class Example {
static void main(String[] args) {
Student st = new Student(); //创建Student类对象
st.StudentID = 1; //给它的属性赋值
st.Marks1 = 10;
st.name="Joe"; //student类通过继承Person类
//获得name属性,并设置它的值
println(st.name);
println(st.DisplayMarks()); //调用方法
}
}
//定义抽象类Person
abstract class Person {
public String name;
public Person() { }
abstract void DisplayMarks(); //抽象方法的定义
}
//Student类继承抽象类Person
class Student extends Person {
int StudentID
int Marks1;
public Student() {
super();
}
//实现父类中的抽象方法
void DisplayMarks() {
println(Marks1);
}
}
接口
接口定义了类需要遵守的规范,接口仅定义需要实现的方法的列表,但是不定义方法实现。接口需要使用interface关键字声明接口,接口的方法总是公开的,在接口中使用受保护或私有方法是一个错误。我们通过下面的示例定义一个接口:
class Example {
static void main(String[] args) {
Student st = new Student();
st.StudentID = 1;
st.Marks1 = 10;
println(st.DisplayMarks());
}
}
//定义接口Marks
interface Marks {
void DisplayMarks(); //定义接口中的方法
}
//定义Student类并实现Marks接口
class Student implements Marks {
int StudentID
int Marks1;
//实现Marks接口中的方法
void DisplayMarks() {
println(Marks1);
}
}
在上面的示例中:
- 创建了一个名为Marks的接口并创建一个名为DisplayMarks的接口方法
- 在Student类中,使用implements关键字来实现接口
- 在实现类中必须为接口中DisplayMarks方法提供实现
1. 下列关于类的说法错误的是?
A 类名可以与类中的方法同名
B abstract可以用于声明类或方法
C 子类的构造器可以访问父类的构造器
D 子类可以重写父类的所有方法
2. 下列关于继承说法正确的是?
A 子类继承父类的public方法和属性
B 子类可以继承父类的私有方法和属性
C 子类只继承父类的方法但不继承父类的属性
D 子类继承父类所有的方法和属性
答案
1=>D 2=>A
11 闭包的概念及使用
Groovy中的闭包完全避免了冗长的代码,而且可以辅助创建轻量级、可复用的代码片段。
通过一个示例来比较传统方式和使用闭包方式的不同
//求1-n中偶数之和
def sum(n){
total=0
for(int i=2;i<=n;i+=2){
total+=i
}
total
}
println "sum of even numbers from 1 to 10 is ${sum(10)}"
上述代码运行了一个for循环,在偶数上迭代求和
//求1-n中偶数的乘积
def product(n){
prod=1
for(int i=2;i<=n;i+=2){
prod *=i
}
prod
}
println "Prod of even numbers from 1 to 10 is ${product(10)}"
我们在求和代码的基础上修改一下,变成求1到10中偶数的乘积。从中提出任务的共同部分就是for循环,把代码改造如下:
def pickEven(n,block){
for(int i=2;i<=n;i+=2){
block(i) //指向闭包的调用
}
}
pickEven(10,{println it}) //{}里面是匿名代码块
在for循环中,变量block保存了一个指向闭包的引用,我们可以像传递对象一样传递闭包。变量名可以是任何合法的变量名,我们把这种匿名代码块称为闭包(Closure)。
通过闭包的方式来求1-10中偶数之和,代码如下:
total=0
pickEven(10,{total+=it})
println "sum of even numbers from 1 to 10 is ${total}"
通过闭包的方式来求1-10中偶数的乘积,代码如下:
product=1
pickEven(10,{product*=it})
println "Prod of even numbers from 1 to 10 is ${product}"
Groovy的闭包不能单独存在,必须附着在方法身上,或者赋给一个变量。