`
leon1509
  • 浏览: 538288 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Groovy 浅入

阅读更多
转贴地址:http://nottiansyf.iteye.com/blog/348077

本文只适合ctrl+F查找,不建议全文阅读


需要将bin路径加入到Path中,然后建立Groovy_Path 设置相应的文件路径

groovyConsole用于简单的进行运行Groovy语言的程序

和Spring整合,可以正常的和Spring进行注解的注入

定义方法和声明一个对象的时候 都可以使用def关键字

使用Groovy重构Java代码 减少代码的冗余

重构JavaBean
方便的重写ToString()方法 在字符串""中使用{属性名} 就可以简单的设置值. {}符号可有可无
def orz="hello orz";
println "orz -- ${orz}";

Groovy会自动的对下面的属性进行Get和Set方法的定义
String manufacturer;
因为 Groovy 的默认属性语义用 public 访问器和存取器自动定义了 private 域
如果需要详细的get或者set的设置, 也可以使用public void setXXX(){}进行自定义
使用protected 等修饰符进行get或者set方法作用域的屏蔽

定律:使普通的编码规则变得简单。

也可以使用 def 关键字来代替特定的类型。

内置的测试,需要继承GroovyTestCase类
然后使用assert 属性==目标 这样进行测试

同样这样的构造函数也是可以动态添加的....注意使用name: 中的:进行属性的复制
def h=new HelloGroovy(name:"tom",age:100,desc:"orz");

Grooving 中使用多态

集合的使用
List ls=[] 声明一个集合,无需导入包
ls << "List1" 给集合添加一个对象 然后可以.size()判断大小

集合中查找
def getBike(serialNo) { bikes.find{it.serialNo == serialNo} }
定义一个方法,传入一个条件进行查询

循环一个集合
ls.each{println it}; 注意使用大括号{}  也可以使用ls.each{e -> println e} 进行代替操作

Map的键值对集合 使用:分别对应
scores = [ "Brett":100, "Pete":"Did not finish", "Andrew":86.87934 ]
println scores["Pete"]
println scores.Pete
创建空集合的方法
emptyMap = [:]
emptyList = []

在Groovy中加载Spring的上下文ApplicationContext
def ctx = new ClassPathXmlApplicationContext("RentABike-context.xml")
def clv = ctx.getBean("commandLineView")
  clv.printAllBikes()
也可以使用注解的注入

读取文件的例子
import java.io.File
new File("maven.xml").eachLine{ line ->
  println "read the following line -> " + line
}
定义一个文件: def nfile = ["c:/dev", "newfile.txt"] as File
..符号的使用
myInclusiveRange = 2..5  
其中2..<5 表示2..4

类似Ruby的操作
5.times{println it};

快速的定义个方法
def squ={it * it}
def void testClosures()
{
println(squ(3));
}
注意it是当前对象的引用,这里指的是值,即传入的参数.
另外种用法
[ 1, 2, 3, 4 ].collect(square) 表示将每个参数分别传入该方法 并进行处理
不过并不会影响到源对象

对键值对的处理
printMapClosure = { key, value -> println key + "=" + value }
[ "yue" : "wu", "lane" : "burks", "sudha" : "saseethiaseeleethialeselan" ].each(printMapClosure)

这个就叫做闭包  关键字用it ,以及key和value
就是生成个函数,然后作为参数传入进行迭代, 用的比较多的地方

声明正则的方式 /字符串/
==~ 进行正则的测试. 类似test方法,返回一个boolean值
正则分组
myRegularExpression = /([a-zA-Z]+), ([a-zA-Z]+): ([0-9]+)/
matcher = ( locationData =~ myRegularExpression )
得到结果
matcher[0][1] 注意这里使用二维数组使用

Groovy SQL
${} 用一个字符串表达一个groovy expression
就是将代码嵌入到字符串中运行

连接数据库的方法
import groovy.sql.Sql
Sql sql = Sql.newInstance("jdbc:mysql://127.0.0.1:3306/demo","root","123","com.mysql.jdbc.Driver")
sql.eachRow("select * from tableName", { println it.id + " -- ${it.firstName} --"} );
sql.close();

这里的Sql类型声明 可以使用def代替
方法前的def可以不写

SQL参数的赋值操作
sql.execute("insert into people (firstName, lastName) "+
  " values (?,?)", [firstName, lastName])

创建Groovy的Servlet-----Groovlets

配置方式
<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>

其中的代码部分
if (!session) {
  session = request.getSession(true);
}

if (!session.counter) {
  session.counter = 1
}

println """
<html>
    <head>
        <title>Groovy Servlet</title>
    </head>
    <body>
Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}
    </body>
</html>
"""
session.counter = session.counter + 1


模板 使用$ 用于生成自定的字符串
import groovy.text.SimpleTemplateEngine

def text = 'Dear "$firstname $lastname",\nSo nice to meet you in <% print city %>.\nSee you in ${month},\n${signed}'

def binding = ["firstname":"Sam", "lastname":"Pullara", "city":"San Francisco", "month":"December", "signed":"Groovy-Dev"]

def engine = new SimpleTemplateEngine()
template = engine.createTemplate(text).make(binding)


as 关键字 类似ActionScript中的类型转换

** 表示平凡运算

.class用于得到指定对象的类型 比如x.class


时间类型
def today = new Date() 声明当前时间
def tomorrow = today + 1 增加一天 ...同样可以减去多天或者一天 plus()表示+ minus()表示减
可以方便的使用
tomorrow.after(today) 进行boolean值的判断
tomorrow.compareTo(today) > 0 功能一样

还有另外种日历类型,暂时省略

Collections 集合类型的时候

定义一个简单的集合
def list = [5, 6, 7, 8] 默认类型是.class为ArrayList
list.set(2, 11) 体会第2索引下的值为11 并返回原来位置的值
[1,2,3,4,5][-1] == 5 反向的读取值
[1,2,3,4,5].getAt(-2) == 4 等同与上面的方法

搜索集合里面的指定值
[1,2,3,4,5].get(-2)  返回0 表示没有找到

[12,3] << 3 直接给集合添加对象
要注意添加一个[]集合给list的时候,不会自动拆包

其他的添加集合的方法
使用+ 或者+=
[1,2] + 3 + [4,5] + 6 == [1, 2, 3, 4, 5, 6]
def a= [1,2,3]; a += 4; a += [5,6]; assert a == [1,2,3,4,5,6]

集合中可以存放不同的类型对象或者值对象
collect方法 用于循环值进行更新,当不修改源对象
[1, 2, 3].collect{ it * 2 } == [2, 4, 6]
也可以直接放入一个对象进行接受返回的直接
def list= []
[1, 2, 3].collect( list ){ it * 2 } == [2, 4, 6]
list=[2,4,6]

集合中的查询, 默认都只返回值
find{it>1} 查询第一个符合条件的结果
findall{it>1}查询所有的结果,并返回
findIndexOf{it in ["c","b"]} 查询在指定集合内的首个索引

条件搜索 every和any
[1,2,3].every{it < 5} 返回true
! [1, 2, 3].every{ it < 3 }
[1, 2, 3].any{ it > 2 }
! [1, 2, 3].any{ it > 3 }

计算集合内的总和
[1,2,3,4,5,6].sum() == 21
可以使用sum{ it=='a'?1: it=='b'?2:} 进行判断后返回值,加入逻辑的使用
字符串的话也会拼接在一起
如果是两个子List,那么会合并成一个List

带参数的Sum 会进行+参数的运算
[1, 2, 3].sum(1000) == 1006
参数的传递,会在{}里面增加一个引用,就是多一个it类型

list.max()  .min() 可以很方便的进行最大小的判断
也可以使用Collections.max( list ) == 10
同样也可以作用在'' 的char类型之上

可以通过it闭包的方式进行更详细的判断
def list2= ['abc', 'z', 'xyzuvw', 'Hello', '321']
list2.max{ it.size() } == 'xyzuvw'
list2.min{ it.size() } == 'z'

运算符的重载
def mc= [compare:{a,b-> a.equals(b)? 0: a<b? -1: 1}] as Comparator
同时可能返回三个结果 0,-1,1

这样就可以直接使用来判断指定大小值,需要将mc作为参数进行传递
def list= [7,4,9,-6,-1,11,2,3,-9,5,-13]
list.max( mc ) == 11
Collections.max( list, mc ) == 11

从List中移除一个对象
['a','b','c','b','b'] - 'c' == ['a','b','b','b']
注意这种移除会移除所有符合条件的对象
['a','b','c','b','b'] - 'b' == ['a','c']
传入一个List用于移除多个
['a','b','c','b','b'] - ['b','c'] == ['a']
minus()方法 起到同样的作用
['a','b','c','b','b'].minus( ['b','c'] ) == ['a']

其他移除的方法
def list= [1,2,3,4,3,2,1]
list -= 3
list == [1,2,4,2,1]
这样会移除所有的3,传入数组也有同样的目的

list.remove(2) 移除指定的索引,并返回被移除的对象
当如果Remove的不是一个数字的时候,将返回boolean值,来判断是否移除成功

list[1..5] 可以使用这种方式方便的截取集合,不过这里获取了一个引用,对截取集合的修改,也会影响到源集合

使用*符号可以很方便的遍历集合

?: 三目操作符

Groovy是实现内部领域特定语言(Domain-Specific Languages)的理想选择

list.clear()方法用于清空集合

判断一个对象是否在集合里面的方法
'a' in ['a','b','c']
['a','b','c'].contains('a')
[1,3,4].containsAll([1,4])

判断对象是否为空
list.isEmpty();

判断对象重复的次数
[1,2,3,3,3,3,4,5].count(3) == 4

判断集合比较重复的对象 交集
[1,2,4,6,8,10,12].intersect([1,3,6,9,12]) == [1,6,12]

判断集合不重复 并集
[1,2,3].disjoint( [4,6,9] )

排序 .sort()方法

自定义排序的方式  闭包的方式
list.sort{ it.size() } == ['z', 'abc', '321', 'Hello', 'xyzuvw']

复杂的排序
list2.sort{a,b-> a.equals(b)? 0: Math.abs(a)<Math.abs(b)? -1: 1 } == [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]

使用Comparator方式排序
def mc= [
  compare: {a,b-> a.equals(b)? 0: Math.abs(a)<Math.abs(b)? -1: 1 }
] as Comparator
assert list2.sort(mc) == [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]

Collections.sort(list3) 也提供了集合类的操作

indexOf()和lastIndexOf() 用于判断该元素的位置, -1表示不存在

取出指定区间的集合,使用索引
('a'..'g')[ 1, 3, 5, 6 ] == ['b','d','f','g']

截取集合中指定的对象
list.retainAll( ['a','e'] )

2进制的操作
Collections.binarySearch([2,5,6,7,9,11,13,26,31,33], 26) == 7

Set的使用
定义一个Set  默认为HashSet类型
def s1= [1,2,3,3,3,4] as Set,

s1.asList() 转换成List 或者toList()方法

数组Array的定义 使用new Object[length]

def a= new Object[4]
同样可以添加不同类型的对象
生成字符串
a.toArrayString() == '{"a", 2, "c", false}'

使用toList()转换成List

多维数组的使用
i= 4
a= new Object[i][i=3]    ==Object[4][3]

Map的使用 键值对的形式  其中Key不能重复,会被自动忽略掉,后面的覆盖掉前面的
def map= ['id':'FX-11', 'name':'Radish', 'no':1234, 99:'Y']

map2.class == null  这里不能使用class获取类型
ap2.getClass() == LinkedHashMap 需要使用这种方法




声明一个空的Map
def map4= [:]

特殊类型的key似乎需要用()包装起来 比如 true false 以及负数,也可以使用null 不过在写的时候也需要使用()

Stringbuffer也可以使用<<进行内容的增加

Characters 类型 区别与String 用'' 而不是"" 长度为16

Strings 类型 字符串 ,包含了Characters
'hello, world' == "hello, world"

"Hello, Groovy's world" == 'Hello, Groovy\'s world'  在character中'需要转译

'''hello,
world''' == 'hello,\nworld' 用''' 表示完整的''类型

$在字符串用来引用变量
7.5d.toString() 方法,不同与Java的方法,返回的也是其类型

s1.codePointAt(3) == 0x10000 返回指定索引位置的字符串的Character字节码

正则的使用

'\07\013\033' ==~ /\a\v\e/  这里会转换成对应的字符 然后进行比较
也可以调用变量进行转换后比较
"${0x1d as char}" =~ /\c]/

'gOoDbYe' ==~ /(?i)goodbye/  表示忽略大小写进行比较
(?x) 表示忽略空格
'abCDefg' ==~ /ab(?i)cd(?-i)efg/ 表示只对一部分忽略大小写
'abCDEfg' ==~ /ab(?i:cde)fg/ 作用同上

'abcdefg' ==~ /(?ix) a b c (?s-ix)defg/ 忽略空格的用法和i基本一样

[]表示多选一  ()表示同时出现

分组查询
def m= java.util.regex.Pattern.compile( /(.*),(.*)/ ).matcher( 'one,two,three' )
m.matches()
m.group(1) == 'one,two'

长字符的比较
import java.util.regex.Pattern
m= Pattern.compile( /(a+)(b+)/ ).matcher( 'aaabbcccaabbb' )
可以使用m.start(2) == 3  m.end(2) 等方法进行访问


使用=~进行方便的匹配
('tone, true, tame, tape, take, tile, time' =~ /t..e/).find{ it[1] == 'a' } == 'tame'

('abcdefg' =~ /bcd|bcdef/)[0] == 'bcd'

贪婪匹配和非贪婪匹配  (区别于结果是否马上返回)
('Friday 13th' =~ /Fri(day)??/)[0][0] == 'Fri'  非贪婪
eachMatch( /".*?"/  为贪婪时匹配

分组例子
def m= ( ~/(.*),(.*)/ ).matcher( 'one,two,three' )
assert m.matches() && m.group(1) == 'one,two' && m.group(2) == 'three'


File类的使用
\符号和:符号的替代  这点和Java一样,为了跨平台
File.separator == '\\'
File.pathSeparator == ':'

声明一个文件
def f= new File('File.txt')

f.absolutePath 文件的完整地址

f.toURI().toString() == 'file:/D:/Groovy/Scripts/File.txt'  用URL的方式返回地址
f.canonicalPath  表示文件可访问的地址

f2.createNewFile() 生成文件 如果文件没有创建

def d1= new File('Directory1')
d1.mkdir()  新建一个文件夹 如果没有创建

new File('D:/Groovy/Scripts').list().toList() 返回该目录下所有文件的列表 List

f2.renameTo( new File('RenamedFile2.txt') ) 重命名

new File('File1.txt').deleteOnExit() 建立临时文件

list= f1.readLines() 读取文件的一行

中文的读取
f2.write('一二三四五', 'unicode') //overwrites existing contents
assert f2.getText('unicode') == '一二三四五'

Streams 流的操作

def fos= new FileOutputStream('TestFile.txt') 打开一个文件输出流

[ 21, 34, 43, 79 ].each{ fos.write(it) } 输出内容
fos.flush() 同样可以调用清空缓存的方法

读取文件中的字节流
new File('TestFile.txt').readBytes().toList() ==
    [ 21, 34, 43, 79, 69, 32, 22, 13, 88 ]

stream记得手动close

new File('TestFile.txt').delete() 删除文件

包装后的文件流处理器
def fw= new FileWriter('TestFile.txt')
new File('TestFile.txt').readLines() == [ 'abcdefghijklmnopqrstuvwxyz' ]  注意s 是读取多行

def fr= new FileReader('TestFile.txt')

文件的简单操作

打开并写入指定的内容,并且会自动关闭
new File('TestFile1.txt').withWriter{ w->
  w<< 'abc' << 'def' //operator syntax
  w.leftShift('ghi').leftShift('jkl') //equivalent method name
}

添加到文件上
new File('TestFile1.txt').withWriterAppend('unicode'){ w->
  w<< 'klmnop' //although appending, unicode marker 0xFEFF also added
}

def fw= new FileWriter('TestFile1.txt')
fw.withWriter{ w->
  ['ab,cd\n' + 'efg\n' + 'hi,jk\n' + 'l', 'mn,op'].each{
    w<< it
  }
} 也可以同样的效果

方便的读取
def list= []
new File('TestFile1.txt').eachLine{
  list<< it
}

new File('TestFile2.txt').text 也可以简单的读取文件中所有的内容,包括了换行 window下为\r\n

new File('TestFile1.txt').readBytes().toList() 字节的方式开始读取

new FileInputStream('TestFile2.txt').getText('unicode') 用于使用UTF编码读取中文

new FileOutputStream('TestFile2.txt').withWriter('unicode'){ w->
  w<< '我是法国人'
}               UTF的方式写入中文

def fos= new FileOutputStream(new File('TestFile.txt'), true) 
用于打开一个文件, 第二个参数默认为true,表示覆盖原文件,false表示使用append的方法

FileWriter的参数相同  其中new File('TestFile.txt') 都可以用直接的字符串代替

bwtr= new File('TheOutput.txt').newWriter('unicode') 给文件增加一个写入器

专门处理行数据的阅读器
def lnr= new LineNumberReader(new FileReader('TheOutput.txt'))

Closures 闭包的使用

def可以同时定义多个变量 使用,号分隔

def c = {...} 为Closure的典型应用 调用的时候使用c()  最后一行为返回的结果 ,有可能返回的是null

def f = { list, value -> list << value } 用于闭包的参数传递  默认为it
f(a,b)

复杂的闭包使用
def runTwice = { a, c -> c(c(a)) }

可以在调用的时候进行命名 然后在闭包中进行引用 只用map方式的参数传递 key:value
def f= {m, i, j-> i + j + m.x + m.y }
assert f(6, x:4, y:3, 7) == 20

定义一个函数
def f(){} 最后一行的为返回值  不需要用return

使用this.&函数名,进行函数的引用
def f(){ 77 }
def g = this.&f
assert g() == 77
也可以不使用特别字符 直接使用
def h = g   也可以

function 函数中也可以传递参数,也支持不同参数的重载
也可以直接使用return返回值,默认是返回最后一行的值

也可以设置返回值类型,不使用def声明函数
void f3(){ 10 }
assert f3() == null //null always returned
返回指定的类型,例子中有void决定

当方法和closure同名的时候,方法会被优先调用

closure可以当做参数被传递

可以给函数的参数定义一个默认值
def dd( a, b=2 ){ "$a, $b" }
当b参数为空的时候,默认就会为2

特别的例子
其中会将X:4 类型的参数自动放置到第一位,并且合并到一起,其他的按照顺序排序,复制给方法里面的参数引用
def f(m, i, j){ i + j + m.x + m.y }
assert f(6, x:4, y:3, 7) == 20

用数组Array接受多个参数
def c( arg, Object[] extras ){
  def list= []
  list<< arg
  extras.each{ list<< it }
  list
}
assert c( 1 )          == [ 1 ]
assert c( 1, 2, 3, 4 ) == [ 1, 2, 3, 4 ]


Classes概念

可以使用闭包访问私有的属性
就是使用try 块定义的局部变量

static private a= 11 定义个类内部的静态私有变量,不能直接访问,不过却可以在同个类的内部使用

Closure c= {it * 3}  可以显式的声明一个闭包对象

oa.class in Object[] 判断一个对象的类型是否为指定类型  关键字 in
分享到:
评论

相关推荐

    apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本

    apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望...

    Java调用Groovy,实时动态加载数据库groovy脚本

    1. 引入Groovy库:在Java项目中添加Groovy的相关依赖,通常是`groovy-all`,确保Java能够访问Groovy运行时环境。 2. 创建GroovyClassLoader:使用这个类加载器可以动态加载和执行Groovy脚本。它继承自Java的...

    groovy-2.3.6-installer

    Groovy是一种动态、开源的编程语言,它是Java平台上的一个JVM(Java Virtual Machine)语言。Groovy结合了Python、Ruby和Perl等脚本语言的简洁性和灵活性,并且完全兼容Java,可以无缝地与Java代码集成。在"groovy-...

    groovy入门经典,groovyeclipse 插件

    Groovy是一种动态、灵活的编程语言,它是Java平台上的一个扩展,可以无缝集成到Java项目中。Groovy的语法简洁,支持面向对象编程、函数式编程,并提供了许多现代语言特性,如闭包和动态类型。这使得Groovy成为快速...

    groovy-3.0.7.msi

    groovy

    Groovy Script 入门

    ### Groovy Script 入门知识点详解 #### 一、Groovy脚本简介 Groovy是一种灵活的面向对象的编程语言,它运行在Java平台上。由于其语法简洁且与Java高度兼容,因此对于Java开发者来说非常容易上手。Groovy不仅支持...

    groovy

    Groovy是一种基于Java平台的、动态的、强大的编程语言,它设计的目标是增强开发者的生产力。Groovy结合了Java的静态类型系统和Python、Ruby等动态语言的灵活性,使得开发者可以更加高效地编写代码。本篇文章将深入...

    JVM 动态执行Groovy脚本的方法

    本文将详细讲解如何使用JVM动态执行Groovy脚本的方法,主要包括利用JShell执行代码、调试模式下动态执行代码以及利用javax.script包执行Groovy脚本。以下是对各知识点的详细说明。 1. 利用JShell执行代码 Java 9 ...

    Groovy入门教程[参照].pdf

    Groovy 入门教程 Groovy 是一种基于 Java 语言的脚本语言,运行在 JVM 中,语法与 Java 相似,但抛弃了 Java 的一些烦琐的语法规则,提供了更加简洁和灵活的编程体验。 Groovy 的特点 1. 简洁的语法:Groovy 语法...

    groovy-all

    Groovy是一种动态、开源的编程语言,它是Java平台上的一个重要的补充。Groovy结合了Python、Ruby和Smalltalk等语言的特性,同时保留了与Java的无缝集成能力,使得它在脚本编写、Web开发、自动化测试等领域有着广泛的...

    groovy-sdk-4.0.3

    Groovy SDK 4.0.3 是一个针对Groovy编程语言的软件开发工具包,它包含了Groovy语言的运行环境和开发所需的各种组件。Groovy是一种动态、灵活的面向对象编程语言,它与Java语法兼容,但提供了更简洁的语法和更强的...

    groovy和Java相互调用1

    标题中的“Groovy和Java相互调用1”指的是在编程时如何在Groovy语言环境中调用Java类,以及反之,如何在Java程序中调用Groovy类。这是一种跨语言交互的方式,特别是在混合使用Groovy和Java的项目中非常常见。 ...

    groovy中map的基本操作1

    在Groovy编程语言中,Map是一种非常重要的数据结构,它用于存储键值对。在本篇博客中,我们将深入探讨Groovy中Map的基本操作,特别是通过标题和描述中给出的例子。 首先,创建一个Map非常简单。例如,`def map = [a...

    eclipse安装groovy插件的步骤

    ### Eclipse 安装 Groovy 插件的详细步骤与使用指南 #### 一、Groovy 插件的安装 为了能够在 Eclipse 中使用 Groovy 进行开发,首先需要安装 Groovy 插件。以下是详细的安装步骤: 1. **下载 GroovyEclipse.zip ...

    groovy速查手册

    ### Groovy速查手册知识点详解 #### 一、Groovy简介与特性 Groovy是一种为Java虚拟机(JVM)设计的动态语言。它具备完全的对象导向性、可选的类型系统、操作符定制能力、简洁的数据类型声明、闭包(Closures)、...

    groovy in action 中文版 2017.11

    Groovy是一种运行在Java虚拟机上的敏捷开发语言,它提供了与Java无缝集成的能力,同时为开发者带来更加简洁和富有表达性的语法。Groovy提供了大量的动态特性,比如动态类型、闭包、元编程能力等,使得编写脚本或应用...

    apache-groovy-sdk-4.0.1下载

    Apache Groovy SDK 4.0.1 是一个重要的软件开发工具包,专为使用Groovy编程语言进行开发的程序员设计。Groovy是一种基于Java平台的动态、灵活的编程语言,它扩展了Java语言的功能,提供了简洁的语法和强大的元编程...

    groovy-3.0.9-API文档-中文版.zip

    赠送jar包:groovy-3.0.9.jar; 赠送原API文档:groovy-3.0.9-javadoc.jar; 赠送源代码:groovy-3.0.9-sources.jar; 赠送Maven依赖信息文件:groovy-3.0.9.pom; 包含翻译后的API文档:groovy-3.0.9-javadoc-API...

Global site tag (gtag.js) - Google Analytics