groovy是一种基于JVM的敏捷开发语言,它结合了Python,Ruby和Smalltalk的许多特性,同时它的代码能够和Java代码很好的结合,并且由于它可以运行在JVM上,所以Groovy可以直接使用其他的Java的jar包
groovy是需要运行在java虚拟机上的,所以需要安装java环境
到java的官网下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
然后解压下载好的压缩包,到硬盘中的任意位置
然后配置环境变量,在终端中输入
subl ~/.zshrc
打开环境变量的配置,并添加java的配置
注: 我安装了 sublim text3,所以使用subl命令,如果没有,请使用vim命令,并且我的终端替换成了zsh,否则环境变量文件是.bash_profile
# JAVA
export JAVA_HOME=/home/chen/Programs/jdk1.8.0_171
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
重启终端,在终端中输入
java -version
看到显示java的相关信息了,则表示java环境变量配置成功
配置Groovy环境的时候主要分成2步:
1. 下载Groovy
2. 解压并配置环境变量
打开groovy的下载页面,我们会发现有很多版本,以目前来说(2018.5.24)它提供了:
- Groovy2.6: Bleeding Edge Vesion
- Groovy2.5: upcoming version
- Groovy2.4: latest stable version
Bleeding Edge Vesion: 这个词不是很好翻译,它指的是那种最新的技术,但是需要承担一定的风险,并且使用者为了追求这种技术的新,也乐于承担这种风险的版本,而钢铁侠不是也有一套血边装甲嘛,好像用的也是bleeding edge这个词,不知道是否也有这种意思
选择下载这个SDK bundle,或者这个页面最上方大大下载按钮,对应的也是最新稳定版的下载链接:
在下载的时候要注意,groovy的版本和jdk的版本是需要对应的
这个indy和non-indy的意思是 需不需要支持Java的InvokeDynamic指令,这是java7中新增的一条字节码指令,如果我们要使用的groovy使用这条指令进行编译的话那么至少需要1.7以上的java版本,而如果不使用该条指令的话,2.4其实也支持到1.6的jvm的,其实在我们写groovy代码时影响不会很大,而且默认使用的也是groovy non-indy的版本,主要影响的是编译的过程,这里建议使用java8以上版本,毕竟java8也出了好长时间了,而且是最新的长期支持版本
下载好了之后,就可以解压到想要的地方了
解压好了之后,和Java一样我们需要配置环境变量:
subl ~/.zshrc
# groovy
export GROOVY_HOME=/home/chen/Programs/groovy-2.4.15
export PATH=${GROOVY_HOME}/bin:$PATH
其实就是把bin文件夹中配置进path中,配置完成后重启终端,然后输入:
groovy -v
我使用的idea进行创建groovy项目,idea的免费版也是支持groovy项目创建的,所以还没有使用idea的小伙伴可以尝试一波
在创建项目的时候,我们注意要选择groovy就可以了,图中2
的部分 如果idea没能正确识别你的groovy和java的安装路径的话,需要手动选一下,之后就一路next,正常的创建项目就可以了
创建好项目之后,我们就可以来写groovy代码了,之前说过groovy是完全兼容Java的,而兼容到什么程度呢,首先我们先按Java代码的语法来写一个Groovy的HelloWorld
我们叫它HelloGroovy
class HelloGroovy {
public static void main(String[] args) {
System.out.println("Hello Groovy");
}
}
运行一下:
可以看到我们可以正常输出了,观察代码发现,这不就完全是Java代码嘛,是的在Groovy中,你甚至可以写Java代码,那么groovy又在java代码的基础上可以简化成什么样子呢?
我们知道,java要求我们:
- 所有的代码必须写在一个类中
- 程序需要有一个主方法来运行
- 每一行语句都以分号结束
但是我们的groovy就要灵活很多了,这些Java中的强制性要求,在groovy中都不存在,修改groovy代码如下:
System.out.println("Hello Groovy")
是的,就这一句,groovy是个脚本语言,它不需要类和方法,程序可以直接由上向下一行一行的执行,并且每一行的后面也不需要分号,我们还可以简化这输出语句:
println "Hello Groovy"
println("Hello")
直接写println即可,并且在groovy中 允许在 顶级表达式
中省略参数列表的小括号,就是你直接调用方法的参数,可以省略,要是方法的参数还是一个方法,就不能省略小括号,例如:
println add(3, 4)
println(add(3, 4))
def add(a, b) {
return a + b
}
这个add的小括号就不能省略,这个后面到方法的时候再说,总之可以发现使用groovy来写java代码,要比纯Java代码简洁很多,恩,语法其实有点类似与Python呢,而Groovy的代码,也需要编译,编译成的文件也是.class文件,这就是为什么Groovy代码能够在JVM上运行,我们直接用idea打开HelloGroovy.class文件,idea会自动把编译后的字节码给我们反编译回来
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class HelloGroovy extends Script {
public HelloGroovy() {
CallSite[] var1 = $getCallSiteArray();
super();
}
public HelloGroovy(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, HelloGroovy.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].call(var1[2].callGetProperty(System.class), "Hello Groovy");
}
}
可以看到所有我们省略的东西,Groovy在编译成字节码的时候,编译器都给我们自动加回来了~
在groovy中可以使用所有Java的数据类型,我们知道在Java中数据类型分为基本数据类型,和引用类型,而在groovy中则没有基本数据类型了,在groovy中,所有的基本数据类型都会在编译的时候转换成其包装类型:
int x = 10
println x
println x.class
可以看到我们定义一个int类型的变量,在输出这个变量的类型的时候,发现它的类型是Integer,即它自动被转换成了引用类型
我们的Java是一门强类型语言,所有的变量的在定义的时候就需要声明好它的数据类型,例如:
public void fun(){
// JAVA代码
String name = "张三";
int age = 18;
}
但是Groovy并不是,它有类型推断机制,就比如我们上面Java代码,到了groovy中,除了可以写成和Java一模一样的以外,我们还可以这样写:
def name = "张三"
def age = 18
println name
println age
println name.class
println age.class
可以看到,无论是String类型,还是int类型,我们都使用的是def来进行定义变量的,而在输出他们的数据类型的时候,Groovy也知道name就是String类型,age就是Integer类型,这就是类型推断,因为我们在定义变量的时候,直接为数据类型进行赋值了,那么编译器就会根据等号右面的值的数据类型,来决定等号左边变量的数据类型
类型推论、类型推断、或隐含类型,是指编程语言在编译期中能够自动推导出值的数据类型的能力,它是一些强静态类型语言的特性。一般而言,函数式编程语言也具有此特性。自动推断类型的能力让很多编程任务变得容易,让程序员可以忽略类型标注的同时仍然允许类型检查
其实Java中的类型推断机制也是逐步被加强的,例如在Java6中你需要这样定义一个String泛型的集合:
// java6
List strings = new ArrayList();
而到了Java7中我们可以这样写:
// java7
List strings = new ArrayList<>();
可以看到编译器已经越来越智能了,不需要我们什么都写出来了,而java8中的lambda表达式也是这种类型推测机制更加强大的体现
那么何时定义变量的时候使用强类型定义,何时定义变量的时候使用def呢,总结下来有2点:
1. 如果变量只用于自己这个类而不会使用于其他模块,则使用def
2. 如果用于其他类的话 那么则使用强类型定义
其实无论是强类型定义还是弱类型定义,都是为了我们写程序更加的方便,易懂,那么如果我们这个模块要给外部使用的时候,如果我们也使用def的话,就会造成调用方的迷惑,不知道传给我们什么类型的参数好了
Java是一门静态类型的语言,即当数据的类型一旦确定下来之后,就不能够在进行更改了,否则编译都过不去:
public class Main {
public static void main(String[] args) {
String s = "张三";
s = 18;
}
}
那么换到我们的groovy中呢,我们在定义变量的时候可以不指定类型,而去使用def,那么我们可以写出如下代码:
def a = "张三"
println a.class
println a
a = 18
println a.class
println a
a = 3.14
println a.class
println a
可以看到在groovy中我们就定义了一个a变量,而这个a变量可以根据运行时值的不同,而动态的改变它的数据类型,我们的Groovy是动态类型的语言,在写起来要比Java灵活很多,那么如果我们使用传统的强类型定义方式去定义一个变量,变量的类型是不是就不能动态的改变了呢,看代码
String a = "张三"
println a.class
println a
a = 18
println a.class
println a
可以看到,强类型定义的方式,我们的数据类型就被固定上了,不能再去改变了,并且如果两个数据类型没办法兼容的话,程序还会报错
int a = 3
a = '张三'
String类型是Java中非常常用的一种数据类型,甚至可以说是最常用的了,Groovy对Java中的String类型做了一些扩展,并把这些扩展封装进了GString(groovy.long.GString)中,就是说在Groovy中的字符串,对应两个类:String和GString,String类和Java中的String没有任何区别,主要看的是Groovy中特殊的GString
在Java中定义String是使用双引号:
String a = "Hello World";
但是在Groovy中就不只只有这一种方法了,在Groovy中可以使用单引号,双引号和三引号(三个单引号:”’)来定义一个String
def a = 'Hello'
def b = "Hello"
def c = '''Hello'''
println a.class
println b.class
println c.class
可以看到这三种方式都可以定义字符串,那么他们的区别是什么呢?
在groovy中使用单引号定义的字符串就是Java中最最普通的字符串,不能插值
在Groovy中如果双引号定义的字符串中没有插值表达式,那么双引号字符串就是普通的String类,如果有差值表达式的存在,则是GString的实例,插值表达式在下面会详细说明
三引号是多行字符串,使用起来和Python是一样的,可以直接在里面写换行,而不需要使用类似于\n
这种的符号来表示换行
def s = '''锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
'''
println s
需要注意的是,这个字符串的每一次换行,其实都是给你自动的添加一个\n的
def s = '''锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
'''
println s.contains('\n')
println s.size()
可以看到字符串中是包含\n
这个字符的,并且字符串的长度是24,20个字加上4个\n
,那么如果我不想换行呢,可以使用反斜杠来取消换行:
def s = '''\
锄禾日当午,\
汗滴禾下土
谁知盘中餐,\
粒粒皆辛苦\
'''
println s
当使用双引号进行表示字符串时,我们可以使用字符串插值,就是用固定格式的字符串来进行占位,实际生成的时候,占位表达式会被动态的替换成响应的字符串
在groovy中使用${表达式}
的形式来进行占位:
def youName = 'cfy'
def sayHello = "Hello: ${youName}"
println sayHello
插值表达式不光可以填写变量的值,还可以计算表达式的值,例如:
def sum = "sum=${5 + 3}"
println sum
我们知道在groovy中如果使用差值表达,那么它的数据类型就会变成GString,而我们在使用的时候,其实是不需要关心其数据类型的,String和GString之间是可以相互融合的:
def sum = "2 + 3 = ${2 + 3}"
def result = echo sum // 在使用时可以相互融合
println(result)
println(result.class)
String echo(String msg){
return msg
}
我们定义了一个方法,接受的参数是String类型的,而sum则是GString类型的,但是发现我们的方法是可以正常调用的
在Groovy中,String类的方法可以有大支分为两类,一类是java.lang.String中的方法,另一类是GString的方法,我们主要看一下GString中的一些常用方法
def str = 'groovy'
// 把str使用-填充至18个字符,str在中间
println str.center(18,'-')
// 在原始字符串左边填充~至指定个个数
println str.padLeft(8, '~')
// 倒叙
println str.reverse()
// 首字母大写
println str.capitalize()
// 判断str是否是数字
println str.isNumber()
str = '89757'
println str.isNumber()
// 将字符串转换成整数类型
def a = str.toInteger()
println(a + ':' + a.class)
在groovy中扩展了字符串的比较:
def str = 'groovy'
def str2 = 'Hello'
println(str > str2) // true
两个字符串比大小,就是逐个比较每一个字符的ascii的大小,在groovy,字符串还可以进行计算,+就是字符串的拼接,-则是在原始字符串中删除指定字符串,只会删除第一个;*就是原始字符串输出几次
str = 'hello'
str2 = str - 'l'
str3 = str * 3
println str2
println str3
在groovy中取子串比较方便,可以使用..操作符来表示范围
def str = 'groovy'
println str[0] // 取一个字符
println str[0..1] // 取范围
println str[-2..-1] // 从倒数第二个字符取到倒数第一个字符
println str[-1..0] // 从倒数第一个字符取到第0个字符,相当于倒叙输出