mysql插入数据后返回自增ID的方法 mysql在插入一条数据后,如何能获得到这个自增id的值呢?即怎么获取设置为自增主键的id(AUTO_INCREMENT)?? 方法一:是使用last_insert_id
1 |
mysql> SELECT LAST_INSERT_ID(); |
产生的自增ID每次连接后保存在服务器中。 这意味着函数向一个给定客户端返回的值是该客户端产生对影响AUTO_INCREMENT列的最新语句第一个 AUTO_INCREMENT值的。 这个值不能被其它客户端影响,即使它们产生它们自己的 AUTO_INCREMENT值。 这个行为保证了你能够找回自己的 ID 而不用担心其它客户端的活动,而且不需要加锁或处理。 每次mysql_query操作在mysql服务器上可以理解为一次“原子”操作, 写操作常常需要锁表的, 是mysql应用服务器锁表不是我们的应用程序锁表。
1 2 3 4 5 6 7 8 9 10 11 12 |
值得注意的是,如果你一次插入了多条记录,这个函数返回的是第一个记录的ID值。 因为LAST_INSERT_ID是基于Connection的,只要每个线程都使用独立的Connection对象, LAST_INSERT_ID函数将返回该Connection对AUTO_INCREMENT列最新的insert or update*作生成的第一个record的ID。 这个值不能被其它客户端(Connection)影响,保证了你能够找回自己的 ID 而不用担心其它客户端的活动,而且不需要加锁。 使用单INSERT语句插入多条记录, LAST_INSERT_ID返回一个列表。 LAST_INSERT_ID 是与table无关的,如果向表a插入数据后,再向表b插入数据,LAST_INSERT_ID会改变。 |
方法二:是使用max(id) 使用last_insert_id是基于连接(connection)的,如果换一个窗口的时候调用则会一直返回0, 如果不是频繁的插入我们也可以使用这种方法来获取返回的id值
1 |
select max(id) from user; |
这个方法的缺点是不适合高并发。如果同时插入的时候返回的值可能不准确。 方法三:是创建一个存储过程,在存储过程中调用先插入再获取最大值的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
DELIMITER $$ DROP PROCEDURE IF EXISTS `test` $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `test`(in name varchar(100),out oid int) BEGIN insert into user(loginname) values(name); select max(id) from user into oid; select oid; END $$ DELIMITER ; call test('gg',@id); |
方法四:使用@@identity
1 |
select @@IDENTITY |
@@identity是表示的是最近一次向具有identity属性(即自增列)的表插入数据时对应的自增列的值,是系统定 义的全局变量。一般系统定义的全局变量都是以@@开头,用户自定义变量以@开头。比如有个表A,它的自增列是id,当向A表插入一行数据后,如果插入数据 后自增列的值自动增加至101,则通过select @@identity得到的值就是101。使用@@identity的前提是在进行insert操作后,执行select @@identity的时候连接没有关闭,否则得到的将是NULL值。 方法五:是使用getGeneratedKeys()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Connection conn = ; Serializable ret = null; PreparedStatement state = .; ResultSet rs=null; try { state.executeUpdate(); rs = state.getGeneratedKeys(); if (rs.next()) { ret = (Serializable) rs.getObject(1); } } catch (SQLException e) { } return ret; |
from:https://blog.csdn.net/zhuchunyan_aijia/article/details/93620357
View Details1、查看配置
1 2 3 4 5 6 |
//查看所有配置 git config -l //查看系统配置 git config --system --list //查看用户自己配置 git config --global --list |
系统文件配置所在位置: 2、配置 用户名与邮箱(用户标识必须) 配置命令
1 2 3 4 |
//配置用户名 git config --global user.name "obgeName" //配置邮箱 git config --global user.email "824307555@qq.com" |
用户自己配置所在位置: from:https://www.cnblogs.com/obge/p/14212013.html
View Details问题描述 在使用SpringBoot 热部署插件 devtools ,同时启动多个Application时,控制台会报这个警告: 问题分析 经查询,发现DevToolsProperties中配置了一个端口,默认是35729。
1 2 3 4 5 6 7 8 9 10 |
public static class Livereload { private boolean enabled = true; private int port = 35729; ... } |
原因:多个端口冲突了,导致热部署插件不生效。 解决方案 在application.properties文件中加上以下配置
1 2 3 4 |
#指定端口,具体视自己情况而定,只要不重复就可以 spring.devtools.livereload.port=35730 |
参考文章 https://blog.csdn.net/xudc0521/article/details/85560221 https://blog.csdn.net/weixin_43852529/article/details/90369869 from:https://www.freesion.com/article/3170268716/
View Detailsdocker部署springboot(默认已经安装好docker) 第一步:构建镜像 创建Dockerfile文件,文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 |
FROM frolvlad/alpine-oraclejdk8:slim VOLUME /tmp ADD index-1.5.10.RELEASE.jar app.jar RUN sh -c 'touch /app.jar' ENV JAVA_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ] |
其中 index-1.5.10.RELEASE.jar是你要发布的jar包。 然后把Dockerfile和index-1.5.10.RELEASE.jar建个文件夹,放到服务器上面。 cd进入你的文件夹,运行以下命令向docker中添加镜像:
1 |
docker build -t jingxiangming . |
执行命令后,你的docker中就添加了名为jingxiangming的镜像。 查看镜像命令:
1 |
docker images //运行命令后,你就可以看到刚刚添加的镜像了 |
第二步:启动容器
1 |
docker run -d -p 8080:8080 jingxiangming |
关于上面的命令,我认为有必要具体讲解一下: -d: 后台运行容器,并返回容器ID; -p: 端口映射,格式为:主机(宿主)端口:容器端口;//意思是第一个访问服务器的端口,第二个8080是服务器本地占用访问的端口 jingxiangming:docker里面的镜像名称 当然除了上面基本的参数外,还有额外的参数,这个就需要大家自己去查阅资料了,我这里只列举比较重要的。 启动成功后,会返回一个容器id,然后就可以测试访问了! 日志查看 查看运行的容器日志:
1 |
sudo docker logs -f -t --tail 行数 容器名 |
1 2 |
//查看容器名 docker ps |
from:https://blog.csdn.net/qq_29611427/article/details/81534037
View Details本文小秋熊介绍在Linux中使用命令启动SpringBoot生成的jar包,并且查看日志的方法。 1.首先将SpringBoot项目打包成JAR包,通过xFTP或者其他工具将JAR包上传到Linux上,然后执行如下命令启动项目: java -jar xxx.jar 该命令启动jar,一旦Xshell窗口关闭,JAR就停止运行了. 如果想让项目在后台一直运行,通过如下命令启动JAR: nohup java -jar xxx.jar > consoleMsg.log 2>&1 & 上面的2 和 1 的意思如下: 0 标准输入(一般是键盘) 1 标准输出(一般是显示屏,是用户终端控制台) 2 标准错误(错误信息输出) 注意:consoleMsg.log文件要先创建,执行命令: touch consoleMsg.log 查看项目运行日志: 1、tailf consoleMsg.log | grep --line-buffered findUserList 实时跟踪日志,这里是只要findUserList 这个方法被运行,就会将它的日志打印出来,用于跟踪特定的日志运行。 --line-buffered 是一行的缓冲区,只要这一行的缓冲区满了就会打印出来,所以可以用于实时监控日志。 2、 tailf -n 500 consoleMsg.log 打印最后500行日志,并且持续跟踪日志。 tail -n 2000 consoleMsg.log | less 分页查看最后2000行日志,并可以使用pageUp,pageDn滚动 3、tail -f consoleMsg.log (常用)直接查看日志末尾,有新日志会实时滚动更新。ctrl + c 退出 其它举例: (1)nohup java -jar xxx.jar >/data/log.log 2>/data/err.log & 解释:标准日志输出到/data/log.log文件,错误日志输出到/data/err.log文件。 (2)nohup java -jar xxx.jar >/data/log.log 2>&1 & 解释:标准日志输出到/data/log.log文件,错误日志重定向也输出到/data/log.log文件。 (3)nohup java -jar xxx.jar >/dev/null 2>/data/err.log & 解释:标准日志输出到/dev/null,也就是不输出标准日志,错误日志输出到/data/err.log文件。 一般采用上面(3)只输出错误日志就可以了,有需要的按照(1)、(2)进行输出。 命令后面加的 & ,可让命令在后台执行,否则关闭会话会停止程序。 […]
View DetailsJava NIO中的Files类(java.nio.file.Files)提供了多种操作文件系统中文件的方法。 Files.exists() Files.exits()方法用来检查给定的Path在文件系统中是否存在。 在文件系统中创建一个原本不存在的Payh是可行的。例如,你想新建一个目录,那么闲创建对应的Path实例,然后创建目录。 由于Path实例可能指向文件系统中的不存在的路径,所以需要用Files.exists()来确认。 下面是一个使用Files.exists()的示例:
1 2 3 4 5 |
Path path = Paths.get("data/logging.properties"); boolean pathExists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS}); |
这个示例中,我们首先创建了一个Path对象,然后利用Files.exists()来检查这个路径是否真实存在。 注意Files.exists()的的第二个参数。他是一个数组,这个参数直接影响到Files.exists()如何确定一个路径是否存在。在本例中,这个数组内包含了LinkOptions.NOFOLLOW_LINKS,表示检测时不包含符号链接文件。 Files.createDirectory() Files.createDirectory()会创建Path表示的路径,下面是一个示例:
1 2 3 4 5 6 7 8 9 10 |
Path path = Paths.get("data/subdir"); try { Path newDir = Files.createDirectory(path); } catch(FileAlreadyExistsException e){ // the directory already exists. } catch (IOException e) { //something else went wrong e.printStackTrace(); } |
第一行创建了一个Path实例,表示需要创建的目录。接着用try-catch把Files.createDirectory()的调用捕获住。如果创建成功,那么返回值就是新创建的路径。 如果目录已经存在了,那么会抛出java.nio.file.FileAlreadyExistException异常。如果出现其他问题,会抛出一个IOException。比如说,要创建的目录的父目录不存在,那么就会抛出IOException。父目录指的是你要创建的目录所在的位置。也就是新创建的目录的上一级父目录。 Files.copy() Files.copy()方法可以吧一个文件从一个地址复制到另一个位置。例如:
1 2 3 4 5 6 7 8 9 10 11 |
Path sourcePath = Paths.get("data/logging.properties"); Path destinationPath = Paths.get("data/logging-copy.properties"); try { Files.copy(sourcePath, destinationPath); } catch(FileAlreadyExistsException e) { //destination file already exists } catch (IOException e) { //something else went wrong e.printStackTrace(); } |
这个例子当中,首先创建了原文件和目标文件的Path实例。然后把它们作为参数,传递给Files.copy(),接着就会进行文件拷贝。 如果目标文件已经存在,就会抛出java.nio.file.FileAlreadyExistsException异常。类似的吐过中间出错了,也会抛出IOException。 覆盖已经存在的文件(Overwriting Existing Files) copy操作可以强制覆盖已经存在的目标文件。下面是具体的示例:
1 2 3 4 5 6 7 8 9 10 11 12 |
Path sourcePath = Paths.get("data/logging.properties"); Path destinationPath = Paths.get("data/logging-copy.properties"); try { Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING); } catch(FileAlreadyExistsException e) { //destination file already exists } catch (IOException e) { //something else went wrong e.printStackTrace(); } |
注意copy方法的第三个参数,这个参数决定了是否可以覆盖文件。 Files.move() Java NIO的Files类也包含了移动的文件的接口。移动文件和重命名是一样的,但是还会改变文件的目录位置。java.io.File类中的renameTo()方法与之功能是一样的。
1 2 3 4 5 6 7 8 9 10 |
Path sourcePath = Paths.get("data/logging-copy.properties"); Path destinationPath = Paths.get("data/subdir/logging-moved.properties"); try { Files.move(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { //moving file failed. e.printStackTrace(); } |
首先创建源路径和目标路径的,原路径指的是需要移动的文件的初始路径,目标路径是指需要移动到的位置。 这里move的第三个参数也允许我们覆盖已有的文件。 Files.delete() Files.delete()方法可以删除一个文件或目录:
1 2 3 4 5 6 7 8 |
Path path = Paths.get("data/subdir/logging-moved.properties"); try { Files.delete(path); } catch (IOException e) { //deleting file failed e.printStackTrace(); } |
首先创建需要删除的文件的path对象。接着就可以调用delete了。 Files.walkFileTree() Files.walkFileTree()方法具有递归遍历目录的功能。walkFileTree接受一个Path和FileVisitor作为参数。Path对象是需要遍历的目录,FileVistor则会在每次遍历中被调用。 下面先来看一下FileVisitor这个接口的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public interface FileVisitor { public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs) throws IOException; public FileVisitResult visitFile( Path file, BasicFileAttributes attrs) throws IOException; public FileVisitResult visitFileFailed( Path file, IOException exc) throws IOException; public FileVisitResult postVisitDirectory( Path dir, IOException exc) throws IOException { } |
FileVisitor需要调用方自行实现,然后作为参数传入walkFileTree().FileVisitor的每个方法会在遍历过程中被调用多次。如果不需要处理每个方法,那么可以继承他的默认实现类SimpleFileVisitor,它将所有的接口做了空实现。 下面看一个walkFileTree()的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Files.walkFileTree(path, new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.println("pre visit dir:" + dir); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("visit file: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { System.out.println("visit file failed: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { System.out.println("post visit directory: " + dir); return FileVisitResult.CONTINUE; } }); |
FileVisitor的方法会在不同时机被调用: preVisitDirectory()在访问目录前被调用。postVisitDirectory()在访问后调用。 visitFile()会在整个遍历过程中的每次访问文件都被调用。他不是针对目录的,而是针对文件的。visitFileFailed()调用则是在文件访问失败的时候。例如,当缺少合适的权限或者其他错误。 上述四个方法都返回一个FileVisitResult枚举对象。具体的可选枚举项包括: CONTINUE TERMINATE SKIP_SIBLINGS SKIP_SUBTREE 返回这个枚举值可以让调用方决定文件遍历是否需要继续。 CONTINE表示文件遍历和正常情况下一样继续。 TERMINATE表示文件访问需要终止。 SKIP_SIBLINGS表示文件访问继续,但是不需要访问其他同级文件或目录。 SKIP_SUBTREE表示继续访问,但是不需要访问该目录下的子目录。这个枚举值仅在preVisitDirectory()中返回才有效。如果在另外几个方法中返回,那么会被理解为CONTINE。 Searching For Files 下面看一个例子,我们通过walkFileTree()来寻找一个README.txt文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Path rootPath = Paths.get("data"); String fileToFind = File.separator + "README.txt"; try { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String fileString = file.toAbsolutePath().toString(); //System.out.println("pathString = " + fileString); if(fileString.endsWith(fileToFind)){ System.out.println("file found at path: " + file.toAbsolutePath()); return FileVisitResult.TERMINATE; } return FileVisitResult.CONTINUE; } }); } catch(IOException e){ e.printStackTrace(); } |
Deleting Directies Recursively Files.walkFileTree()也可以用来删除一个目录以及内部的所有文件和子目。Files.delete()只用用于删除一个空目录。我们通过遍历目录,然后在visitFile()接口中三次所有文件,最后在postVisitDirectory()内删除目录本身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Path rootPath = Paths.get("data/to-delete"); try { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("delete file: " + file.toString()); Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); System.out.println("delete dir: " + dir.toString()); return FileVisitResult.CONTINUE; } }); } catch(IOException e){ e.printStackTrace(); } |
from:https://www.cnblogs.com/liangblog/p/8920579.html
View Details原文链接 译者:章筱虎 Path接口是java NIO2的一部分。首次在java 7中引入。Path接口在java.nio.file包下,所以全称是java.nio.file.Path。 java中的Path表示文件系统的路径。可以指向文件或文件夹。也有相对路径和绝对路径之分。绝对路径表示从文件系统的根路径到文件或是文件夹的路径。相对路径表示从特定路径下访问指定文件或文件夹的路径。相对路径的概念可能有点迷糊。不用担心,我将在本文的后面详细介绍相关细节。 不要将文件系统的path和操作系统的环境变量path搞混淆。java.nio.file.Path接口和操作系统的path环境变量没有任何关系。 在很多方面,java.nio.file.Path接口和java.io.File有相似性,但也有一些细微的差别。在很多情况下,可以用Path来代替File类。 创建Path实例 为了使用java.nio.file.Path实例,必须首先创建它。可以使用Paths 类的静态方法Paths.get()来产生一个实例。以下是示例:
1 2 3 4 5 6 7 8 9 10 11 |
import java.nio.file.Path; import java.nio.file.Paths; public class PathExample { public static void main(String[] args) { Path path = Paths.get("c:\\data\\myfile.txt"); } } |
请注意例子开头的两个import语句。想要使用Paths类和Path接口,必须首先引入相应包。其次,注意Paths.get(“c:\\data\\myfile.txt”)的用法。其使用了Paths.get方法创建了Path的实例。它是一个工厂方法。 创建绝对路径Path 调用传入绝对路径当做参数的Paths.get()工厂方法,就可以生成绝对路径Path。示例如下:
1 |
Path path = Paths.get("c:\\data\\myfile.txt"); |
示例中的绝对路径是c:\data\myfile.txt。有两个\字符的原因是第一个\是转义字符,表示紧跟着它的字符需要被转义。\\表示需要向字符串中写入一个\字符。 上文示例的path是windows下的路径。在Unix系统(Linux,MacOS,FreeBSD等)中,上文中的path是这样的:
1 |
Path path = Paths.get("/home/jakobjenkov/myfile.txt"); |
/home/jakobjenkov/myfile.txt就称作绝对路径。 如果把以/开头path的格式运行在windows系统中,系统会将其解析为相对路径。例如:
1 |
/home/jakobjenkov/myfile.txt |
将会被解析为路径是在C盘。对应的绝对路径是: C:/home/jakobjenkov/myfile.txt 创建相对路径Path 相对路径指从一个已确定的路径开始到某一文件或文件夹的路径。将确定路径和相对路径拼接起来就是相对路径对应的绝对路径地址。 java NIO Path类也能使用相对路径。可以通过Paths.get(basePath, relativePath)创建一个相对路径Path。示例如下:
1 2 3 |
Path projects = Paths.get("d:\\data", "projects"); Path file = Paths.get("d:\\data", "projects\\a-project\\myfile.txt"); |
第一个例子创建了一个指向d:\data\projects文件夹的实例。第二个例子创建了一个指向 d:\data\projects\a-project\myfile.txt 文件的实例。 当使用相对路径的时候,可以使用如下两种特别的符号。它们是: . .. .表示当前路径。例如,如果以如下方式创建一个相对路径:
1 2 |
Path currentDir = Paths.get("."); System.out.println(currentDir.toAbsolutePath()); |
创建的Path实例对应的路径就是运行这段代码的项目工程目录。 如果.用在路径中,则其表示的就是当前路径下。示例: Path currentDir = Paths.get(“d:\\data\\projects\.\a-project”); 对应的就是如下路径 d:\data\projects\a-project ..表示父类目录。示例: Path parentDir = Paths.get(“..”); Path对应的路径是当前运行程序目录的上级目录。 如果在path中使用..,表示上级目录的含义。例如:
1 2 |
String path = "d:\\data\\projects\\a-project\\..\\another-project"; Path parentDir2 = Paths.get(path); |
对应的绝对路径地址为: d:\data\projects\another-project 在a-project目录后面的..符号,将指向的目录修改为projects目录,因此,最终path指向another-project目录。 .和..都可以在Paths.get()的双形参方法中使用。示例:
1 2 3 4 |
Path path1 = Paths.get("d:\\data\\projects", ".\\a-project"); Path path2 = Paths.get("d:\\data\\projects\\a-project", "..\\another-project"); |
下面介绍NIO 的Path类有关相对路径的其他使用方法。 Path.normalize() Path 的normalize()方法可以标准化路径。标准化的含义是路径中的.和..都被去掉,指向真正的路径目录地址。下面是Path.normalize()示例:
1 2 3 4 5 6 7 8 |
String originalPath = "d:\\data\\projects\\a-project\\..\\another-project"; Path path1 = Paths.get(originalPath); System.out.println("path1 = " + path1); Path path2 = path1.normalize(); System.out.println("path2 = " + path2); |
上文示例,首先创建了一个包含..字符的路径地址。之后输出此路径。 之后,调用normalize方法,返回一个新的path对象。输出新对象的路径。 输出结果如下:
1 2 |
path1 = d:\data\projects\a-project\..\another-project path2 = d:\data\projects\another-project |
如你所见,标准化后的路径不再包含 a-project\..部分,因为它是多余的。 from:https://ifeve.com/java-nio-path-2/
View Details一、环境搭建 安装好JDK环境 到groovy官网下载groovySDK,解压到合适位置 groovy官网:http://www.groovy-lang.org/ 安装后的文件如上图所示,我们需要关注的是bin和doc文件夹下的内容,其它文件夹下是一些配置和groovy自带的一些jar包 配置groovy环境变量 二、与Java的不同之处 1、默认 imports 所有这些包和类都是默认导入的,您不必使用显式import语句来使用它们:
1 2 3 4 5 6 7 8 |
java.io.* java.lang.* java.math.BigDecimal java.math.BigInteger java.net.* java.util.* groovy.lang.* groovy.util.* |
2、运行时分派 在Groovy中,将在运行时选择将被调用的方法。 这称为运行时分派或Multi-methods。 这意味着将基于运行时参数的类型来选择方法。 在Java中,则是根据声明的类型,在编译时选择方法。 下面的代码,以Java代码编写,可以在Java和Groovy中编译,但它的行为会有所不同:
1 2 3 4 5 6 7 8 |
int method(String arg) { return 1; } int method(Object arg) { return 2; } Object o = "Object"; int result = method(o); |
在Java中, 您将得到:2 而在Groovy中:1 这是因为Java将使用静态信息类型,即o被声明为Object,而Groovy将在运行时选择该方法被实际调用时。 因为它是用String调用的,所以调用String版本。 3、数组初始化 在Groovy中,{…}块是为闭包而保留的。 这意味着您不能使用以下语法创建数组:
1 |
int [] array = {1,2,3} |
你必须使用:
1 |
int [] array = [1,2,3] |
4、自动资源管理块 Groovy不支持Java 7中的ARM(自动资源管理)块。 相反,Groovy提供了依赖闭包的各种方法,它们具有相同的效果,同时更加方便。 例如:
1 2 3 4 5 6 7 8 9 10 11 |
Path file = Paths.get("/path/to/file"); Charset charset = Charset.forName("UTF-8"); try (BufferedReader reader = Files.newBufferedReader(file, charset)) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } |
可以这样写:
1 2 3 |
new File('/path/to/file').eachLine('UTF-8') { println it } |
或者,如果你想要一个更接近Java的版本:
1 2 3 4 5 |
new File('/path/to/file').withReader('UTF-8') { reader -> reader.eachLine { println it } } |
5、Lambdas Java 8支持lambdas和方法引用:
1 2 |
Runnable run = () -> System.out.println("Run"); list.forEach(System.out::println); |
Java 8 lambdas可以或多或少被认为是匿名内部类。 Groovy不支持该语法,但是可以使用闭包:
1 2 |
Runnable run = { println 'run' } list.each { println it } // or list.each(this.&println) |
6、GStrings 由于双引号字符串字面量被解释为GString,Groovy将在GString和String之间自动转换 Groovy中的单引号用于String,双引号结果是String或GString,取决于文字中是否有插值。
1 2 3 |
assert 'c'.getClass()==String assert "c".getClass()==String assert "c${1}".getClass() in GString |
只有在赋给char类型的变量时,Groovy会自动将单字符String转换为char。 当调用类型为char的参数的方法时,我们需要显式转换或确保该值已预先转换。
1 2 3 4 5 6 7 8 9 |
char a='a' assert Character.digit(a, 16)==10 : 'But Groovy does boxing' assert Character.digit((char) 'a', 16)==10 try { assert Character.digit('a', 16)==10 assert false: 'Need explicit cast' } catch(MissingMethodException e) { } |
Groovy支持两种类型的转换,在转换为char的情况下,在转换multi-char 时存在微妙的差别。 Groovy风格的转换是更宽松的,将采取第一个字符,而C风格的转换将失败,异常。
1 2 3 4 5 6 7 8 9 10 11 12 |
// for single char strings, both are the same assert ((char) "c").class==Character assert ("c" as char).class==Character // for multi char strings they are not try { ((char) 'cx') == 'c' assert false: 'will fail - not castable' } catch(GroovyCastException e) { } assert ('cx' as char) == 'c' assert 'cx'.asType(char) == 'c' |
7、原始和封装 因为Groovy使用Objects来做每一件事,它对原始的引用自动包装。 因此,它不遵循Java的扩展优先于装箱。 这里有一个使用int的例子
1 2 3 4 5 6 7 8 9 10 11 12 |
int i m(i) //这是Java将调用的方法,因为扩展优先于装箱。 void m(long l) { println "in m(long)" } //这是Groovy实际调用的方法,因为所有的基本引用都使用它们的包装类。 void m(Integer i) { println "in m(Integer)" } |
8、==的行为 在Java中==表示对象的原始类型或标识的相等性。 在Groovy ==翻译为a.compareTo(b)== 0,如果他们是可比较的,否则a.equals(b)。 如果要检查身份,有is方法,例如a.is(b) 三、基础语法 1、动态类型 Groovy定义变量时:可以用Groovy风格的def声明,不指定类型;也可以兼容Java风格,指定变量类型;甚至还可以省略def或类型。
1 2 3 |
def t1 = 't1' String t2 = 't2' t3 = 't3' |
[…]
View Details