Groovy

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的保留字

abstractassertbreakcase
catchclassconstcontinue
defdefaultdoelse
enumextendsfinalfinally
forgotoifimplements
importinstanceofinterfacenative
newnullnon-sealedpackage
publicprotectedprivatereturn
staticstrictfpsuperswitch
synchronizedthisthreadsafethrow
throwstransienttrywhile

一般来说这些保留字不能用于定义变量、方法。如果用双引号引起来,也可以作为方法名,但不推荐这么做。例如:

 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

各种类型数值的后缀

类型后缀
BigIntegerG or g
LongL or l
IntegerI or i
BigDecimalG or g
DoubleD or d
FloatF 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的闭包不能单独存在,必须附着在方法身上,或者赋给一个变量。

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值