diff --git a/.gitignore b/.gitignore index 18e8fdf..78f111f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,133 @@ # Created by .ignore support plugin (hsz.mobi) +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.project +*.iml +.idea/ +.vscode/ +.classpath +### Eclipse template + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ +cmake-build-release/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar ### Java template # Compiled class file *.class diff --git a/README.md b/README.md index 8734f0d..7b5ea89 100644 --- a/README.md +++ b/README.md @@ -2,38 +2,43 @@ 这个仓库存储了『跟上Java 8』视频课程源码。 -java 8 改变了我们思考和编码的方式,在这里你会看到很多关于 Java 8 的文章和教程,如何使用它的各种功能,以及如何让你的编程更轻松! +Java8 改变了我们思考和编码的方式,在这里你可以学习到 Java8 中的核心特性以及使用场景,跟我学习 Java8,让你的编程更轻松! [![Travis Build](https://travis-ci.org/biezhi/learn-java8.svg?branch=master)](https://travis-ci.org/biezhi/learn-java8) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) [![License](https://img.shields.io/badge/license-BSD--3-blue.svg)](https://github.com/biezhi/learn-java8/blob/master/LICENSE) [![@biezhi on zhihu](https://img.shields.io/badge/zhihu-%40biezhi-red.svg)](https://www.zhihu.com/people/biezhi) -[![](https://img.shields.io/github/followers/biezhi.svg?style=social&label=Follow%20Me)](https://github.com/biezhi) +[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/biezhii.svg?style=social&label=Follow%20Twitter)](https://twitter.com/biezhii) +[![](https://img.shields.io/github/followers/biezhi.svg?style=social&label=Follow%20Github)](https://github.com/biezhi) 课程的讲授方式原则是:**What**、**Why**、**How** 我们会说清楚每个技能是什么,为什么是这样,以及编码演示他们的使用方法以及场景, 最后会讲解 Java 8 中的最佳实践以及正确的函数式编程姿势。 -## 教程大纲 +## 课程大纲 -| 课时数 | 课时标题 | 在线播放 | 源码位置 | +| 课时数 | 课时标题 | 在线播放 | 源码位置 | |:-----:|:--------|:-------|:-------| -|第 1 课 | [课程介绍]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 2 课 | [Java 8 的发展]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 3 课 | [理解 lambda]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 4 课 | [初尝 lambda]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 5 课 | [默认方法的妙用]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 6 课 | [干掉空指针之 Optional]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 7 课 | [理解 Stream]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 8 课 | [Stream API(上)]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 9 课 | [Stream API(下)]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 10 课 | [新的日期时间 API]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 11 课 | [并发增强]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 12 课 | [CompletableFuture]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 13 课 | [Nashorn 引擎]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 14 课| [Java 8 最佳实践]() | [哔哩哔哩]() ¦ [Youtube]() | | -|第 15 课| [函数式编程的正确姿势]() | [哔哩哔哩]() ¦ [Youtube]() | | +|第 1 课 | [课程介绍](https://github.com/biezhi/learn-java8) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051513399&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_1.html#page=1) ¦ [Youtube](https://youtu.be/A733pQxiEDk) | 无 | +|第 2 课 | [Java 8 的发展](https://github.com/biezhi/learn-java8/blob/master/java8-growing/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051508577&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_2.html#page=2) ¦ [Youtube](https://youtu.be/fHhgm1AZzhs) | [java8-growing](https://github.com/biezhi/learn-java8/tree/master/java8-growing/src/main/java/io/github/biezhi/java8/growing) | +|第 3 课 | [理解 lambda](https://github.com/biezhi/learn-java8/blob/master/java8-lambda/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051516241&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_3.html#page=3) ¦ [Youtube](https://youtu.be/VkdMeFEGDH8) | [lambda1](https://github.com/biezhi/learn-java8/tree/master/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1) | +|第 4 课 | [初尝 lambda](https://github.com/biezhi/learn-java8/blob/master/java8-lambda/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051511463&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_4.html#page=4) ¦ [Youtube](https://youtu.be/X7Zv5vygjTc) | [lambda2](https://github.com/biezhi/learn-java8/tree/master/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson2) | +|第 5 课 | [lambda 进阶](https://github.com/biezhi/learn-java8/blob/master/java8-lambda/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051518174&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_5.html#page=5) ¦ [Youtube](https://youtu.be/3G83it4IASc) | [lambda3](https://github.com/biezhi/learn-java8/tree/master/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3) | +|第 6 课 | [默认方法的妙用](https://github.com/biezhi/learn-java8/blob/master/java8-default-methods/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051518175&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_6.html#page=6) ¦ [Youtube](https://youtu.be/sAuEnkWezDM) | [default-method](https://github.com/biezhi/learn-java8/tree/master/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods) | +|第 7 课 | [干掉空指针之 Optional](https://github.com/biezhi/learn-java8/blob/master/java8-optional/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051511464&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_7.html#page=7) ¦ [Youtube](https://youtu.be/br4kqCXPB9A) | [optional](https://github.com/biezhi/learn-java8/tree/master/java8-default-methods/src/main/java/io/github/biezhi/java8/optional) | +|第 8 课 | [理解 Stream](https://github.com/biezhi/learn-java8/blob/master/java8-stream/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051555343&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_8.html#page=8) ¦ [Youtube](https://youtu.be/NB9mGlNMl-w) | [stream](https://github.com/biezhi/learn-java8/tree/master/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1) | +|第 9 课 | [Stream API(上)](https://github.com/biezhi/learn-java8/blob/master/java8-stream/README.md#%E4%BD%BF%E7%94%A8%E6%B5%81) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051566020&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_9.html#page=9) ¦ [Youtube](https://youtu.be/mGwFJERNzmY) | [stream](https://github.com/biezhi/learn-java8/blob/master/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2) | +|第 10 课 | [Stream API(下)](https://github.com/biezhi/learn-java8/blob/master/java8-stream/README.md#collector-%E6%94%B6%E9%9B%86) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051571684&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_10.html#page=10) ¦ [Youtube](https://youtu.be/iubE0ezu-xI) | [stream](https://github.com/biezhi/learn-java8/tree/master/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3) | +|第 11 课 | [新的日期时间 API](https://github.com/biezhi/learn-java8/blob/master/java8-datetime-api/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051571688&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_11.html#page=11) ¦ [Youtube](https://youtu.be/hKXJvh-id1E) | [datetime](https://github.com/biezhi/learn-java8/tree/master/java8-datetime-api/src/main/java/io/github/biezhi/datetime) | +|第 12 课 | [并发增强](https://github.com/biezhi/learn-java8/blob/master/java8-concurrent/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051682806&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_12.html#page=12) ¦ [Youtube](https://youtu.be/OYkToWIDEEI) | [concurrent](https://github.com/biezhi/learn-java8/tree/master/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent) | +|第 13 课 | [CompletableFuture](https://github.com/biezhi/learn-java8/blob/master/java8-completablefuture/README.md) | [网易云课堂](http://study.163.com/course/courseLearn.htm?courseId=1005047049&utm_campaign=commission&utm_source=cp-400000000397038&utm_medium=share#/learn/video?lessonId=1051908792&courseId=1005047049) ¦ [哔哩哔哩](https://www.bilibili.com/video/av19287893/index_13.html#page=13) ¦ [Youtube](https://youtu.be/4reRygD1dGo) | [completablefuture](https://github.com/biezhi/learn-java8/tree/master/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture) | +|第 14 课 | Nashorn 引擎 | 网易云课堂 ¦ 哔哩哔哩 ¦ Youtube | | +|第 15 课| Java 8 最佳实践 | 网易云课堂 ¦ 哔哩哔哩 ¦ Youtube | | +|第 16 课| 函数式编程的正确姿势 | 网易云课堂 ¦ 哔哩哔哩 ¦ Youtube | | + +> 💡 点击课时标题即可查看课程大纲 +> +> 💊 建议课程食用方式: x1.25倍速效果更佳 ## 相关资源 @@ -42,17 +47,16 @@ java 8 改变了我们思考和编码的方式,在这里你会看到很多关 ## 交流 -如果你有什么好的想法或建议可以给我发送邮件,我的电子邮件是 `biezhi.me#gmail.com`,或者在 Github 上发起一个 [issue](https://github.com/biezhi/lets-java8/issues/new)。 -期待未来有更多的分享可以加入这个QQ群:**663887729**,保持 8/2 原则,少斗图多交流。 +在学习 Java8 过程中有疑问可以在QQ群:**663887729** 交流或者在视频的评论区留言。 +你也可以在 Github 上发起一个 [issue](https://github.com/biezhi/learn-java8/issues/new) 提问。 ## 捐赠作者 我们提供的所有视频和源码都是免费的,如果你在这个过程中学习到了技能可以扫描下方二维码赞赏作者。 -`支付宝.jpg | 微信.jpg` + ## 授权许可 除特别声明外,本套教程中的内容使用 [CC BY-SA 3.0 License](https://creativecommons.org/licenses/by-sa/3.0/deed.zh)(创作共用 署名-相同方式共享 3.0 许可协议)授权, 代码遵循 [BSD 3-Clause](https://opensource.org/licenses/BSD-3-Clause) License(3 项条款的 BSD 许可协议)。 - diff --git a/java8-best-practice/pom.xml b/java8-best-practice/pom.xml index 631d97f..3986f8f 100644 --- a/java8-best-practice/pom.xml +++ b/java8-best-practice/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-completablefuture/README.md b/java8-completablefuture/README.md new file mode 100644 index 0000000..720b159 --- /dev/null +++ b/java8-completablefuture/README.md @@ -0,0 +1,114 @@ +# CompletableFuture + +## 为什么要引入 CompletableFuture + +- Future.get() 方法会阻塞线程,一直阻塞,直到其所对应的任务完成或因为异常退出。在不恰当的地方调用此方法会因为线程阻塞而降低系统响应度。就像文中提到的。 +- Future.get(long, TimeUnit) 可以一定的时间内超时退出,而不会像前一个方法那样一直阻塞线程。但是这对系统响应性的改进是治标不治本。 +- Java 8 引入了 CompletableFuture,可用通过 CompletableFuture thenAccept(Consumer action) 方法异步触发 send(Response) 方法。这时 serve() 方法会很快结束。而当 responseFuture 所对应的任务完成时,send(Response) 方法便会被调用,其执行线程同 responseFuture 所对应的任务的执行线程相同。 +- 对于上一点,有人会问,何必这么麻烦?直接在 responseFuture 所对应任务 (Runnable or Callable) 里面调用 send(Response) 方法。各种在实际工作中可能会遇到的问题暂且不说。只说两点: + 1. responseFuture 所对应任务是不可修改的,比如调用自第三方模块 + 2. 即便代码可修改,但在 responseFuture 所对应的任务中去调用 send(Response) 方法表明的含义是后者的功能从属于前者。这可能从业务角度上看是不合理的。即在本例中,responseFuture 所对应的任务和 send(Response) 方法在业务角度讲是属于同一级的。违反这一点会对代码可读性和可维护性不利。 + +## 创建 CompletableFuture + +以下四个静态方法用来为一段异步执行的代码创建 `CompletableFuture` 对象: + +```java +static CompletableFuture runAsync(Runnable runnable) +static CompletableFuture runAsync(Runnable runnable, Executor executor) +static CompletableFuture supplyAsync(Supplier supplier) +static CompletableFuture supplyAsync(Supplier supplier, Executor executor) +``` + +以 `Async` 结尾并且没有指定 `Executor` 的方法会使用 `ForkJoinPool.commonPool()` 作为它的线程池执行异步代码。 + +## 计算结果完成时的处理 + +当 `CompletableFuture` 的计算结果完成,或者抛出异常的时候,我们可以执行特定的 `Action`。 + +```javap +CompletableFuture whenComplete(BiConsumer action) +CompletableFuture whenCompleteAsync(BiConsumer action) +CompletableFuture whenCompleteAsync(BiConsumer action, Executor executor) +CompletableFuture exceptionally(Function fn) +``` + +同时进行计算和转换 + +```java + CompletableFuture handle(BiFunction fn) + CompletableFuture handleAsync(BiFunction fn) + CompletableFuture handleAsync(BiFunction fn, Executor executor) +``` + +## 结果转换(map) + +```java + CompletableFuture thenApply(Function fn) + CompletableFuture thenApplyAsync(Function fn) + CompletableFuture thenApplyAsync(Function fn, Executor executor) +``` + +## flatMap + +```java + CompletableFuture thenCompose(Function> fn) + CompletableFuture thenComposeAsync(Function> fn) + CompletableFuture thenComposeAsync(Function> fn, Executor executor) +``` + +## 消耗型 + +```java +CompletableFuture thenAccept(Consumer action) +CompletableFuture thenAcceptAsync(Consumer action) +CompletableFuture thenAcceptAsync(Consumer action, Executor executor) +``` + +`thenAccept(Consumer action)` 这个方法的命名采用了类似 Promise 的命名风格。 +如果把这个方法命名为 addListener 会更容易理解,但是命名为 addListener 不能体现出 thenAccept 能返回 CompletableFuture 从而形成链式调用的特点。 + +当两个 `CompletionStage` 都正常完成计算的时候,执行一个 `Runnable` + +```java + CompletableFuture thenAcceptBoth(CompletionStage other, BiConsumer action) + CompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action) + CompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor) + CompletableFuture runAfterBoth(CompletionStage other, Runnable action) +``` + +对上一步的计算结果不关心,执行下一个操作 + +```java +CompletableFuture thenRun(Runnable action) +CompletableFuture thenRunAsync(Runnable action) +CompletableFuture thenRunAsync(Runnable action, Executor executor) +``` + +## 组合 + +```java + CompletableFuture thenCombine(CompletionStage other, BiFunction fn) + CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn) + CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor) +``` + +## Either + +```java + CompletableFuture acceptEither(CompletionStage other, Consumer action) + CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action) + CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action, Executor executor) + CompletableFuture applyToEither(CompletionStage other, Function fn) + CompletableFuture applyToEitherAsync(CompletionStage other, Function fn) + CompletableFuture applyToEitherAsync(CompletionStage other, Function fn, Executor executor) +``` + +## allOf、anyOf + +```java +static CompletableFuture allOf(CompletableFuture... cfs) +static CompletableFuture anyOf(CompletableFuture... cfs) +``` + +[CompletableFuture 的 20 个例子](https://zhuanlan.zhihu.com/p/34921166) diff --git a/java8-completablefuture/pom.xml b/java8-completablefuture/pom.xml new file mode 100644 index 0000000..62b3144 --- /dev/null +++ b/java8-completablefuture/pom.xml @@ -0,0 +1,14 @@ + + + + learn-java8 + io.github.biezhi + 1.0-SNAPSHOT + + 4.0.0 + + java8-completablefuture + + \ No newline at end of file diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture1.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture1.java new file mode 100644 index 0000000..8c31b9c --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture1.java @@ -0,0 +1,19 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; + +/** + * 创建 CompletableFuture + * + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture1 { + + public static void main(String[] args) { + CompletableFuture helloFuture = CompletableFuture.runAsync(() -> System.out.println("hello future")); + + CompletableFuture integerCompletableFuture = CompletableFuture.supplyAsync(() -> 2333); + + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture10.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture10.java new file mode 100644 index 0000000..6c6a7a3 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture10.java @@ -0,0 +1,42 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture10 { + + private static CompletableFuture m1(){ + return CompletableFuture.supplyAsync(() -> { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 2333; + }); + } + private static CompletableFuture m2(){ + return CompletableFuture.supplyAsync(() -> { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 8877; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + long start = System.currentTimeMillis(); + CompletableFuture.anyOf(m1(), m2()) + .thenRun(() -> { + System.out.println(System.currentTimeMillis() - start); + }).get() + ; + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture2.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture2.java new file mode 100644 index 0000000..91b4e51 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture2.java @@ -0,0 +1,43 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * 处理计算结果 + * + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture2 { + + public static void main(String[] args) { + CompletableFuture uCompletableFuture = CompletableFuture.supplyAsync(() -> { + System.out.println("开始执行运算"); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + e.printStackTrace(); + } + int a = 1/0; + System.out.println("执行结束"); + return 2333; + }); + + try { + Integer result = uCompletableFuture.whenComplete((a, b) -> { + System.out.println("Result: " + a); + System.out.println("Exception: " + b); + }).exceptionally(e -> { + System.out.println(e.getMessage()); + return 666; + }).get(); + System.out.println(result); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture3.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture3.java new file mode 100644 index 0000000..5bbf46e --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture3.java @@ -0,0 +1,25 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; + +/** + * 结果转换 + * + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture3 { + + public static void main(String[] args) { + try { + String result = CompletableFuture.supplyAsync(() -> 2333) + .thenApply(String::valueOf).get(); + System.out.println(result); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture4.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture4.java new file mode 100644 index 0000000..85f093c --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture4.java @@ -0,0 +1,24 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * 扁平转换 + * + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture4 { + + public static void main(String[] args) { + try { + String s = CompletableFuture.supplyAsync(() -> 23333) + .thenCompose(t -> CompletableFuture.supplyAsync(() -> t + "ddd")) + .get(); + System.out.println(s); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture5.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture5.java new file mode 100644 index 0000000..fdb1c7d --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture5.java @@ -0,0 +1,18 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * 消费结果 + * + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture5 { + + public static void main(String[] args) throws ExecutionException, InterruptedException { + CompletableFuture.supplyAsync(() -> 9999) + .thenAccept(System.out::println).get(); + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture6.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture6.java new file mode 100644 index 0000000..11ba721 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture6.java @@ -0,0 +1,19 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture6 { + + public static void main(String[] args) throws ExecutionException, InterruptedException { + CompletableFuture.supplyAsync(() -> 9999) + .thenAcceptBoth(CompletableFuture.supplyAsync(() -> "7878"), (a, b) -> { + System.out.println("a = " + a); + System.out.println("b = " + b); + }).get(); + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture7.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture7.java new file mode 100644 index 0000000..a0c9009 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture7.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture7 { + + public static void main(String[] args) throws ExecutionException, InterruptedException { + CompletableFuture.supplyAsync(() -> { + try { + System.out.println("开始执行了"); + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 9999; + }).thenRun(() -> { + System.out.println("执行结束了"); + }).get(); + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture8.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture8.java new file mode 100644 index 0000000..c0ccf33 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture8.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture8 { + + public static void main(String[] args) { + try { + String s = CompletableFuture.supplyAsync(() -> 23333) + .thenCombine(CompletableFuture.supplyAsync( () -> "8898" ), (a, b) -> { + System.out.println("a =" + a); + System.out.println("b =" + b); + return a + b; + }) + .get(); + System.out.println(s); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture9.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture9.java new file mode 100644 index 0000000..ce1e4e8 --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/CompletableFuture9.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class CompletableFuture9 { + + private static CompletableFuture m1(){ + return CompletableFuture.supplyAsync(() -> { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 2333; + }); + } + private static CompletableFuture m2(){ + return CompletableFuture.supplyAsync(() -> { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 8877; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + m1().acceptEither(m2(), t -> { + System.out.println("t = " + t); + }).get(); + } +} diff --git a/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/Future1.java b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/Future1.java new file mode 100644 index 0000000..b03353d --- /dev/null +++ b/java8-completablefuture/src/main/java/io/github/biezhi/java8/completablefuture/Future1.java @@ -0,0 +1,38 @@ +package io.github.biezhi.java8.completablefuture; + +import java.util.concurrent.*; + +/** + * @author biezhi + * @date 2018/3/25 + */ +public class Future1 { + + public static void main(String[] args) throws ExecutionException, InterruptedException { + ExecutorService executorService = Executors.newFixedThreadPool(2); + + Future submit = executorService.submit(() -> { + TimeUnit.SECONDS.sleep(3); + return 100; + }); + +// try { +// Integer result = submit.get(); +// System.out.println(result); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } catch (ExecutionException e) { +// e.printStackTrace(); +// } + + while(!submit.isDone()){ + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Integer result = submit.get(); + System.out.println(result); + } +} diff --git a/java8-concurrent/README.md b/java8-concurrent/README.md new file mode 100644 index 0000000..e9a42ff --- /dev/null +++ b/java8-concurrent/README.md @@ -0,0 +1,311 @@ +# java8 concurrent + +1. [知乎专栏 - Threads和Executors](https://zhuanlan.zhihu.com/p/33266682) +2. [知乎专栏 - 同步和锁](http://zhuanlan.zhihu.com/p/33267015) +3. [知乎专栏 - 原子变量和 ConcurrentMap](https://zhuanlan.zhihu.com/p/33267165) + +## Thread 和 Runnable + +所有的现代操作系统都通过进程和线程来支持并发。进程是通常彼此独立运行的程序的实例,比如,如果你启动了一个Java程序,操作系统产生一个新的进程,与其他程序一起并行执行。 +在这些进程的内部,我们使用线程并发执行代码,因此,我们可以最大限度的利用CPU可用的核心(core)。 + +Java从JDK1.0开始执行线程。在开始一个新的线程之前,你必须指定由这个线程执行的代码,通常称为task。这可以通过实现Runnable——一个定义了一个无返回值无参数的 `run()` 方法的函数接口。 + +## 线程池 + +在执行一个异步任务或并发任务时,往往是通过直接 `new Thread()` 方法来创建新的线程,这样做弊端较多,更好的解决方案是合理地利用线程池,线程池的优势很明显,如下: + +1. 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗; +2. 提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行; +3. 方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率; +4. 更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。 + +### 线程池用法 + +**newCachedThreadPool** + +创建一个可缓存的无界线程池,该方法无参数。当线程池中的线程空闲时间超过60s则会自动回收该线程,当任务超过线程池的线程数则创建新线程。线程池的大小上限为 `Integer.MAX_VALUE`,可看做是无限大。 + +```java +public void cachedThreadPoolDemo(){ + ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); + for (int i = 0; i < 5; i++) { + final int index = i; + + cachedThreadPool.execute(new Runnable() { + + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+", index="+index); + } + }); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} +``` + +运行结果: + +```bash +pool-1-thread-1, index=0 +pool-1-thread-1, index=1 +pool-1-thread-1, index=2 +pool-1-thread-1, index=3 +pool-1-thread-1, index=4 +``` + +从运行结果可以看出,整个过程都在同一个线程pool-1-thread-1中运行,后面线程复用前面的线程。 + +**newFixedThreadPool** + +创建一个固定大小的线程池,该方法可指定线程池的固定大小,对于超出的线程会在 `LinkedBlockingQueue` 队列中等待。 + +```java +public void fixedThreadPoolDemo(){ + ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); + for (int i = 0; i < 6; i++) { + final int index = i; + + fixedThreadPool.execute(new Runnable() { + + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+", index="+index); + } + }); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} +``` + +运行结果: + +```bash +pool-1-thread-1, index=0 +pool-1-thread-2, index=1 +pool-1-thread-3, index=2 +pool-1-thread-1, index=3 +pool-1-thread-2, index=4 +pool-1-thread-3, index=5 +``` + +从运行结果可以看出,线程池大小为3,每休眠1s后将任务提交给线程池的各个线程轮番交错地执行。线程池的大小设置,可参数 `Runtime.getRuntime().availableProcessors()`。 + +**newSingleThreadExecutor** + +创建一个只有线程的线程池,该方法无参数,所有任务都保存队列LinkedBlockingQueue中,等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。 + +```java +public void singleThreadExecutorDemo(){ + ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); + for (int i = 0; i < 3; i++) { + final int index = i; + + singleThreadExecutor.execute(new Runnable() { + + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+", index="+index); + } + }); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} +``` + +运行结果: + +```bash +pool-1-thread-1, index=0 +pool-1-thread-1, index=1 +pool-1-thread-1, index=2 +``` + +从运行结果可以看出,所有任务都是在单一线程运行的。 + +**newScheduledThreadPool** + +创建一个可定时执行或周期执行任务的线程池,该方法可指定线程池的核心线程个数。 + +```java +public void scheduledThreadPoolDemo(){ + ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); + //定时执行一次的任务,延迟1s后执行 + scheduledThreadPool.schedule(new Runnable() { + + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+", delay 1s"); + } + }, 1, TimeUnit.SECONDS); + + //周期性地执行任务,延迟2s后,每3s一次地周期性执行任务 + scheduledThreadPool.scheduleAtFixedRate(new Runnable() { + + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+", every 3s"); + } + }, 2, 3, TimeUnit.SECONDS); +} +``` + +运行结果: + +```bash +pool-1-thread-1, delay 1s +pool-1-thread-1, every 3s +pool-1-thread-2, every 3s +pool-1-thread-2, every 3s +... +``` + +- `schedule(Runnable command, long delay, TimeUnit unit)`: 延迟一定时间后执行 `Runnable` 任务; +- `schedule(Callable callable, long delay, TimeUnit unit)`: 延迟一定时间后执行 `Callable` 任务; +- `scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)`: 延迟一定时间后,以间隔period时间的频率周期性地执行任务; +- `scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit)`: 与 `scheduleAtFixedRate()` 方法很类似, +但是不同的是scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个任务开始执行的间隔, +而scheduleAtFixedRate()方法的周期时间间隔是以上一个任务开始执行到下一个任务开始执行的间隔,也就是这一些任务系列的触发时间都是可预知的。 + +> ScheduledExecutorService 功能强大,对于定时执行的任务,建议多采用该方法。 + +**方法对比** + +| 工厂方法 | corePoolSize | maximumPoolSize | keepAliveTime | workQueue | +|:-----:|:--------|:-------|:-------|:-------| +| `newCachedThreadPool` | 0 | Integer.MAX_VALUE | 60s `SynchronousQueue` | +| `newFixedThreadPool` | nThreads | nThreads | 0 | `LinkedBlockingQueue` | +| `newSingleThreadExecutor` | 1 | 1 | 0 | `LinkedBlockingQueue` | +| `newScheduledThreadPool` | corePoolSize | Integer.MAX_VALUE | 0 | `DelayedWorkQueue` | + +其他参数都相同,其中线程工厂的默认类为 `DefaultThreadFactory`,线程饱和的默认策略为 `ThreadPoolExecutor.AbortPolicy`。 + +## 简单使用 Lock 锁 + +Java 5 中引入了新的锁机制——java.util.concurrent.locks 中的显式的互斥锁:Lock 接口,它提供了比 `synchronized` 更加广泛的锁定操作。 +Lock 接口有 3 个实现它的类:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。 +lock 必须被显式地创建、锁定和释放,为了可以使用更多的功能,一般用 ReentrantLock 为其实例化。为了保证锁最终一定会被释放(可能会有异常发生),要把互斥区放在 try 语句块内,并在 finally 语句块中释放锁,尤其当有 return 语句时,return 语句必须放在 try 字句中,以确保 unlock()不会过早发生,从而将数据暴露给第二个任务。因此,采用 lock 加锁和释放锁的一般形式如下: + +```java +//默认使用非公平锁,如果要使用公平锁,需要传入参数true +Lock lock = new ReentrantLock(); +lock.lock(); +try { + // 更新对象的状态 + // 捕获异常,必要时恢复到原来的不变约束 + // 如果有return语句,放在这里 +} finally { + //锁必须在finally块中释放 + lock.unlock(); +} +``` + +可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。 +在JAVA环境下 `ReentrantLock` 和 `synchronized` 都是可重入锁。 + +**ReentrantReadWriteLock** + +读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。 +如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。 +总之,读的时候上读锁,写的时候上写锁! + +`ReentrantReadWriteLock` 会使用两把锁来解决问题,一个读锁,一个写锁 + +- 线程进入读锁的前提条件 + - 没有其他线程的写锁 + - 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个 +- 线程进入写锁的前提条件 + - 没有其他线程的读锁 + - 没有其他线程的写锁 + +## StampedLock + +`StampedLock` 是 java 8 在 `java.util.concurrent.locks` 新增的一个API。 + +`ReentrantReadWriteLock` 在沒有任何读锁和写锁时,才可以取得写入锁,这可用于实现了悲观读取。 +然而,如果读取很多,写入很少的情况下,使用 `ReentrantReadWriteLock` 可能会使写入线程遭遇饥饿问题,也就是写入线程无法竞争到锁定而一直处于等待状态。 +`StampedLock` 有三种模式的锁,用于控制读取/写入访问,StampedLock 的状态由版本和模式组成。 +锁获取操作返回一个用于展示和访问锁状态的票据(stamp)变量,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。 +在读锁上分为悲观锁和乐观锁,锁释放以及其他相关方法需要使用邮戳(stamps)变量作为参数,如果他们和当前锁状态不符则失败,这三种模式为: + +- 写入:方法writeLock可能为了获取独占访问而阻塞当前线程,返回一个stamp变量,能够在unlockWrite方法中使用从而释放锁。也提供了tryWriteLock。 +当锁被写模式所占有,没有读或者乐观的读操作能够成功。 +- 读取:方法readLock可能为了获取非独占访问而阻塞当前线程,返回一个stamp变量,能够在unlockRead方法中用于释放锁。也提供了tryReadLock。 +- 乐观读取:方法 `tryOptimisticRead` 返回一个非 0 邮戳变量,仅在当前锁没有以写入模式被持有。如果在获得stamp变量之后没有被写模式持有,方法validate将返回true。 +这种模式可以被看做一种弱版本的读锁,可以被一个写入者在任何时间打断。乐观读取模式仅用于短时间读取操作时经常能够降低竞争和提高吞吐量。 + +> 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。 +> 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。 +> Java synchronized 就属于悲观锁的一种实现,每次线程要修改数据时都先获得锁,保证同一时刻只有一个线程能操作数据,其他线程则会被block。 + +> 乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。 +乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。 +> 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 + +## AtomicInteger + +JDK1.5之后的java.util.concurrent.atomic包里,多了一批原子处理类。 +AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。 +主要用于在高并发环境下的高效程序处理,来帮助我们简化同步处理. + +AtomicInteger,一个提供原子操作的Integer的类。 +在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。 +而AtomicInteger则通过一种线程安全的加减操作接口。 + +```java +public final int get() //获取当前的值 +public final int getAndSet(int newValue)//获取当前的值,并设置新的值 +public final int getAndIncrement() //获取当前的值,并自增 +public final int getAndDecrement() //获取当前的值,并自减 +public final int getAndAdd(int delta) //获取当前的值,并加上预期的值 +``` + +## LongAccumulator + +`LongAdder` 是jdk1.8提供的累加器,基于 `Striped64` 实现。 +它常用于状态采集、统计等场景。 +AtomicLong也可以用于这种场景,但在线程竞争激烈的情况下,LongAdder要比AtomicLong拥有更高的吞吐量,但会耗费更多的内存空间。 + +`LongAccumulator` 和 `LongAdder` 类似,也基于Striped64实现。但要比LongAdder更加灵活(要传入一个函数接口), +LongAdder相当于是LongAccumulator的一种特例。 + +## Semaphore + +Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。 +很多年以来,我都觉得从字面上很难理解Semaphore所表达的含义,只能把它比作是控制流量的红绿灯,比如XX马路要限制流量,只允许同时有一百辆车在这条路上行使, +其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入XX马路, +但是如果前一百辆中有五辆车已经离开了XX马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程在执行, +离开马路就表示线程执行完成,看见红灯就表示线程被阻塞,不能执行。 + +**应用场景** + +Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。 +假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取, +但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个, +这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。 +这个时候,我们就可以使用Semaphore来做流控。 + +## 参考资料 + +- [Java线程池分析](http://gityuan.com/2016/01/16/thread-pool/) +- [浅谈Java中的锁](http://zhwbqd.github.io/2015/02/13/lock-in-java.html) +- [Java原子操作AtomicInteger的用法](https://www.jianshu.com/p/509aca840f6d) +- [Jdk1.8 JUC源码增量解析 LongAdder和LongAccumulator](http://brokendreams.iteye.com/blog/2259858) +- [StampedLock将是解决同步问题的新宠](http://www.importnew.com/14941.html) +- [Java8 StampedLock](https://coderbee.net/index.php/concurrent/20140628/947) +- [控制并发线程数的Semaphore](http://ifeve.com/tag/semaphore/) \ No newline at end of file diff --git a/java8-concurrent/pom.xml b/java8-concurrent/pom.xml index e708de7..99e10a3 100644 --- a/java8-concurrent/pom.xml +++ b/java8-concurrent/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Atomic1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Atomic1.java new file mode 100644 index 0000000..712c8fa --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Atomic1.java @@ -0,0 +1,76 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +/** + * 原子变量 + *

+ * AtomicInteger + * LongAdder + * LongAccumulator + * + * @author biezhi + * @date 2018/3/5 + */ +public class Atomic1 { + + private static final int NUM_INCREMENTS = 1000; + + private static AtomicInteger atomicInt = new AtomicInteger(0); + + public static void main(String[] args) { + testIncrement(); +// testAccumulate(); +// testUpdate(); + } + + private static void testUpdate() { + atomicInt.set(0); + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> { + Runnable task = () -> + atomicInt.updateAndGet(n -> n + 2); + executor.submit(task); + }); + + ConcurrentUtils.stop(executor); + + System.out.format("Update: %d\n", atomicInt.get()); + } + + private static void testAccumulate() { + atomicInt.set(0); + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> { + Runnable task = () -> + atomicInt.accumulateAndGet(i, (n, m) -> n + m); + executor.submit(task); + }); + + ConcurrentUtils.stop(executor); + + System.out.format("Accumulate: %d\n", atomicInt.get()); + } + + private static void testIncrement() { + atomicInt.set(0); + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(atomicInt::incrementAndGet)); + + ConcurrentUtils.stop(executor); + + System.out.format("Increment: Expected=%d; Is=%d\n", NUM_INCREMENTS, atomicInt.get()); + } +} diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentHashMap1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentHashMap1.java new file mode 100644 index 0000000..3265838 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentHashMap1.java @@ -0,0 +1,74 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; + +public class ConcurrentHashMap1 { + + public static void main(String[] args) { + System.out.println("Parallelism: " + ForkJoinPool.getCommonPoolParallelism()); + + testForEach(); +// testSearch(); +// testReduce(); + } + + private static void testReduce() { + ConcurrentHashMap map = new ConcurrentHashMap<>(); + map.putIfAbsent("foo", "bar"); + map.putIfAbsent("han", "solo"); + map.putIfAbsent("r2", "d2"); + map.putIfAbsent("c3", "p0"); + + String reduced = map.reduce(1, (key, value) -> key + "=" + value, + (s1, s2) -> s1 + ", " + s2); + + System.out.println(reduced); + } + + private static void testSearch() { + ConcurrentHashMap map = new ConcurrentHashMap<>(); + map.putIfAbsent("foo", "bar"); + map.putIfAbsent("han", "solo"); + map.putIfAbsent("r2", "d2"); + map.putIfAbsent("c3", "p0"); + + System.out.println("\nsearch()\n"); + + String result1 = map.search(1, (key, value) -> { + System.out.println(Thread.currentThread().getName()); + if (key.equals("foo") && value.equals("bar")) { + return "foobar"; + } + return null; + }); + + System.out.println(result1); + + System.out.println("\nsearchValues()\n"); + + String result2 = map.searchValues(1, value -> { + System.out.println(Thread.currentThread().getName()); + if (value.length() > 3) { + return value; + } + return null; + }); + + System.out.println(result2); + } + + private static void testForEach() { + ConcurrentHashMap map = new ConcurrentHashMap<>(); + map.putIfAbsent("foo", "bar"); + map.putIfAbsent("han", "solo"); + map.putIfAbsent("r2", "d2"); + map.putIfAbsent("c3", "p0"); + + map.forEach(1, (key, value) -> System.out.printf("key: %s; value: %s; thread: %s\n", key, value, Thread.currentThread().getName())); +// map.forEach(5, (key, value) -> System.out.printf("key: %s; value: %s; thread: %s\n", key, value, Thread.currentThread().getName())); + + System.out.println(map.mappingCount()); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentUtils.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentUtils.java new file mode 100644 index 0000000..f95c1f1 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/ConcurrentUtils.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 并发工具类 + * + * @author biezhi + * @date 2018/3/11 + */ +public class ConcurrentUtils { + + public static void stop(ExecutorService executor) { + try { + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } + catch (InterruptedException e) { + System.err.println("termination interrupted"); + } + finally { + if (!executor.isTerminated()) { + System.err.println("killing non-finished tasks"); + } + executor.shutdownNow(); + } + } + + public static void sleep(int seconds) { + try { + TimeUnit.SECONDS.sleep(seconds); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + + +} diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors1.java new file mode 100644 index 0000000..c3ae935 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors1.java @@ -0,0 +1,43 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class Executors1 { + + public static void main(String[] args) { + test1(3); +// test1(7); + } + + private static void test1(long seconds) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.submit(() -> { + try { + TimeUnit.SECONDS.sleep(seconds); + String name = Thread.currentThread().getName(); + System.out.println("task finished: " + name); + } catch (InterruptedException e) { + System.err.println("task interrupted"); + } + }); + stop(executor); + } + + static void stop(ExecutorService executor) { + try { + System.out.println("attempt to shutdown executor"); + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.err.println("termination interrupted"); + } finally { + if (!executor.isTerminated()) { + System.err.println("killing non-finished tasks"); + } + executor.shutdownNow(); + System.out.println("shutdown finished"); + } + } +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors2.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors2.java new file mode 100644 index 0000000..e90ecd2 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors2.java @@ -0,0 +1,66 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.*; + +public class Executors2 { + + public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { + test1(); +// test2(); +// test3(); + } + + private static void test3() throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newFixedThreadPool(1); + + Future future = executor.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + return 123; + } catch (InterruptedException e) { + throw new IllegalStateException("task interrupted", e); + } + }); + + future.get(1, TimeUnit.SECONDS); + } + + private static void test2() throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newFixedThreadPool(1); + + Future future = executor.submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); + return 123; + } catch (InterruptedException e) { + throw new IllegalStateException("task interrupted", e); + } + }); + + executor.shutdownNow(); + future.get(); + } + + private static void test1() throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newFixedThreadPool(1); + + Future future = executor.submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); + return 123; + } catch (InterruptedException e) { + throw new IllegalStateException("task interrupted", e); + } + }); + + System.out.println("future done: " + future.isDone()); + + Integer result = future.get(); + + System.out.println("future done: " + future.isDone()); + System.out.print("result: " + result); + + executor.shutdownNow(); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors3.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors3.java new file mode 100644 index 0000000..155d665 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Executors3.java @@ -0,0 +1,97 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +public class Executors3 { + + public static void main(String[] args) throws InterruptedException, ExecutionException { + test1(); +// test2(); +// test3(); + +// test4(); +// test5(); + } + + private static void test5() throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newWorkStealingPool(); + + List> callables = Arrays.asList( + callable("task1", 2), + callable("task2", 1), + callable("task3", 3)); + + String result = executor.invokeAny(callables); + System.out.println(result); + + executor.shutdown(); + } + + private static Callable callable(String result, long sleepSeconds) { + return () -> { + TimeUnit.SECONDS.sleep(sleepSeconds); + return result; + }; + } + + private static void test4() throws InterruptedException { + ExecutorService executor = Executors.newWorkStealingPool(); + + List> callables = Arrays.asList( + () -> "task1", + () -> "task2", + () -> "task3"); + + executor.invokeAll(callables) + .stream() + .map(future -> { + try { + return future.get(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + }) + .forEach(System.out::println); + + executor.shutdown(); + } + + private static void test3() { + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + Runnable task = () -> { + try { + TimeUnit.SECONDS.sleep(2); + System.out.println("Scheduling: " + System.nanoTime()); + } catch (InterruptedException e) { + System.err.println("task interrupted"); + } + }; + + executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS); + } + + private static void test2() { + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime()); + int initialDelay = 0; + int period = 1; + executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS); + } + + private static void test1() throws InterruptedException { + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime()); + int delay = 3; + ScheduledFuture future = executor.schedule(task, delay, TimeUnit.SECONDS); + + TimeUnit.MILLISECONDS.sleep(1337); + + long remainingDelay = future.getDelay(TimeUnit.MILLISECONDS); + System.out.printf("Remaining Delay: %sms\n", remainingDelay); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock1.java new file mode 100644 index 0000000..631daae --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock1.java @@ -0,0 +1,42 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.IntStream; + +public class Lock1 { + + private static final int NUM_INCREMENTS = 10000; + + private static ReentrantLock lock = new ReentrantLock(); + + private static int count = 0; + + private static void increment() { + lock.lock(); + try { + count++; + } finally { + lock.unlock(); + } + } + + public static void main(String[] args) { + testLock(); + } + + private static void testLock() { + count = 0; + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(Lock1::increment)); + + ConcurrentUtils.stop(executor); + + System.out.println(count); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock2.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock2.java new file mode 100644 index 0000000..f5dffa9 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock2.java @@ -0,0 +1,33 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReentrantLock; + +public class Lock2 { + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + ReentrantLock lock = new ReentrantLock(); + + executor.submit(() -> { + lock.lock(); + try { + ConcurrentUtils.sleep(1); + } finally { + lock.unlock(); + } + }); + + executor.submit(() -> { + System.out.println("Locked: " + lock.isLocked()); + System.out.println("Held by me: " + lock.isHeldByCurrentThread()); + boolean locked = lock.tryLock(); + System.out.println("Lock acquired: " + locked); + }); + + ConcurrentUtils.stop(executor); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock3.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock3.java new file mode 100644 index 0000000..e0e47fa --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock3.java @@ -0,0 +1,44 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class Lock3 { + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + Map map = new HashMap<>(); + + ReadWriteLock lock = new ReentrantReadWriteLock(); + + executor.submit(() -> { + lock.writeLock().lock(); + try { + ConcurrentUtils.sleep(1); + map.put("foo", "bar"); + } finally { + lock.writeLock().unlock(); + } + }); + + Runnable readTask = () -> { + lock.readLock().lock(); + try { + System.out.println(map.get("foo")); + ConcurrentUtils.sleep(1); + } finally { + lock.readLock().unlock(); + } + }; + executor.submit(readTask); + executor.submit(readTask); + + ConcurrentUtils.stop(executor); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock4.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock4.java new file mode 100644 index 0000000..6e933b5 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock4.java @@ -0,0 +1,43 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.StampedLock; + +public class Lock4 { + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + Map map = new HashMap<>(); + + StampedLock lock = new StampedLock(); + + executor.submit(() -> { + long stamp = lock.writeLock(); + try { + ConcurrentUtils.sleep(1); + map.put("foo", "bar"); + } finally { + lock.unlockWrite(stamp); + } + }); + + Runnable readTask = () -> { + long stamp = lock.readLock(); + try { + System.out.println(map.get("foo")); + ConcurrentUtils.sleep(1); + } finally { + lock.unlockRead(stamp); + } + }; + executor.submit(readTask); + executor.submit(readTask); + + ConcurrentUtils.stop(executor); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock5.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock5.java new file mode 100644 index 0000000..b78a6fd --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock5.java @@ -0,0 +1,41 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.StampedLock; + +public class Lock5 { + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StampedLock lock = new StampedLock(); + + executor.submit(() -> { + long stamp = lock.tryOptimisticRead(); + try { + System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); + ConcurrentUtils.sleep(1); + System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); + ConcurrentUtils.sleep(2); + System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); + } finally { + lock.unlock(stamp); + } + }); + + executor.submit(() -> { + long stamp = lock.writeLock(); + try { + System.out.println("Write Lock acquired"); + ConcurrentUtils.sleep(2); + } finally { + lock.unlock(stamp); + System.out.println("Write done"); + } + }); + + ConcurrentUtils.stop(executor); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock6.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock6.java new file mode 100644 index 0000000..af7977f --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Lock6.java @@ -0,0 +1,36 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.StampedLock; + +public class Lock6 { + + private static int count = 0; + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StampedLock lock = new StampedLock(); + + executor.submit(() -> { + long stamp = lock.readLock(); + try { + if (count == 0) { + stamp = lock.tryConvertToWriteLock(stamp); + if (stamp == 0L) { + System.out.println("Could not convert to write lock"); + stamp = lock.writeLock(); + } + count = 23; + } + System.out.println(count); + } finally { + lock.unlock(stamp); + } + }); + + ConcurrentUtils.stop(executor); + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAccumulator1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAccumulator1.java new file mode 100644 index 0000000..2aac7e4 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAccumulator1.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.function.LongBinaryOperator; +import java.util.stream.IntStream; + +public class LongAccumulator1 { + + public static void main(String[] args) { + testAccumulate(); + } + + private static void testAccumulate() { + LongBinaryOperator op = (x, y) -> 2 * x + y; + LongAccumulator accumulator = new LongAccumulator(op, 1L); + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, 10) + .forEach(i -> executor.submit(() -> accumulator.accumulate(i))); + + ConcurrentUtils.stop(executor); + + System.out.format("Add: %d\n", accumulator.getThenReset()); + } +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAdder1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAdder1.java new file mode 100644 index 0000000..a031154 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/LongAdder1.java @@ -0,0 +1,40 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.LongAdder; +import java.util.stream.IntStream; + +public class LongAdder1 { + + private static final int NUM_INCREMENTS = 10000; + + private static LongAdder adder = new LongAdder(); + + public static void main(String[] args) { + testIncrement(); + testAdd(); + } + + private static void testAdd() { + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(() -> adder.add(2))); + + ConcurrentUtils.stop(executor); + + System.out.format("Add: %d\n", adder.sumThenReset()); + } + + private static void testIncrement() { + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(adder::increment)); + + ConcurrentUtils.stop(executor); + + System.out.format("Increment: Expected=%d; Is=%d\n", NUM_INCREMENTS, adder.sumThenReset()); + } +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore1.java new file mode 100644 index 0000000..edcf297 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore1.java @@ -0,0 +1,46 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +public class Semaphore1 { + + private static final int NUM_INCREMENTS = 10000; + + private static Semaphore semaphore = new Semaphore(1); + + private static int count = 0; + + public static void main(String[] args) { + testIncrement(); + } + + private static void testIncrement() { + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(Semaphore1::increment)); + + ConcurrentUtils.stop(executor); + + System.out.println("Increment: " + count); + } + + private static void increment() { + boolean permit = false; + try { + permit = semaphore.tryAcquire(5, TimeUnit.SECONDS); + count++; + } catch (InterruptedException e) { + throw new RuntimeException("could not increment"); + } finally { + if (permit) { + semaphore.release(); + } + } + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore2.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore2.java new file mode 100644 index 0000000..2dcbb74 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Semaphore2.java @@ -0,0 +1,41 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +public class Semaphore2 { + + private static Semaphore semaphore = new Semaphore(5); + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(10); + + IntStream.range(0, 10) + .forEach(i -> executor.submit(Semaphore2::doWork)); + + ConcurrentUtils.stop(executor); + } + + private static void doWork() { + boolean permit = false; + try { + permit = semaphore.tryAcquire(1, TimeUnit.SECONDS); + if (permit) { + System.out.println("Semaphore acquired"); + ConcurrentUtils.sleep(5); + } else { + System.out.println("Could not acquire semaphore"); + } + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } finally { + if (permit) { + semaphore.release(); + } + } + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized1.java new file mode 100644 index 0000000..6894030 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized1.java @@ -0,0 +1,52 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.IntStream; + +public class Synchronized1 { + + private static final int NUM_INCREMENTS = 10000; + + private static int count = 0; + + public static void main(String[] args) { + testSyncIncrement(); + testNonSyncIncrement(); + } + + private static void testSyncIncrement() { + count = 0; + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(Synchronized1::incrementSync)); + + ConcurrentUtils.stop(executor); + + System.out.println(" Sync: " + count); + } + + private static void testNonSyncIncrement() { + count = 0; + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(Synchronized1::increment)); + + ConcurrentUtils.stop(executor); + + System.out.println("NonSync: " + count); + } + + private static synchronized void incrementSync() { + count = count + 1; + } + + private static void increment() { + count = count + 1; + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized2.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized2.java new file mode 100644 index 0000000..27e9613 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Synchronized2.java @@ -0,0 +1,36 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.IntStream; + +public class Synchronized2 { + + private static final int NUM_INCREMENTS = 10000; + + private static int count = 0; + + public static void main(String[] args) { + testSyncIncrement(); + } + + private static void testSyncIncrement() { + count = 0; + + ExecutorService executor = Executors.newFixedThreadPool(2); + + IntStream.range(0, NUM_INCREMENTS) + .forEach(i -> executor.submit(Synchronized2::incrementSync)); + + ConcurrentUtils.stop(executor); + + System.out.println(count); + } + + private static void incrementSync() { + synchronized (Synchronized2.class) { + count = count + 1; + } + } + +} \ No newline at end of file diff --git a/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Threads1.java b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Threads1.java new file mode 100644 index 0000000..c3f9bd5 --- /dev/null +++ b/java8-concurrent/src/main/java/io/github/biezhi/java8/concurrent/Threads1.java @@ -0,0 +1,58 @@ +package io.github.biezhi.java8.concurrent; + +import java.util.concurrent.TimeUnit; + +public class Threads1 { + + public static void main(String[] args) { + test1(); +// test2(); +// test3(); + } + + private static void test3() { + Runnable runnable = () -> { + try { + System.out.println("Foo " + Thread.currentThread().getName()); + TimeUnit.SECONDS.sleep(1); + System.out.println("Bar " + Thread.currentThread().getName()); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + }; + + Thread thread = new Thread(runnable); + thread.start(); + } + + private static void test2() { + Runnable runnable = () -> { + try { + System.out.println("Foo " + Thread.currentThread().getName()); + Thread.sleep(1000); + System.out.println("Bar " + Thread.currentThread().getName()); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + }; + + Thread thread = new Thread(runnable); + thread.start(); + } + + private static void test1() { + Runnable runnable = () -> { + String threadName = Thread.currentThread().getName(); + System.out.println("Hello " + threadName); + }; + + runnable.run(); + + Thread thread = new Thread(runnable); + thread.start(); + + System.out.println("Done!"); + } +} \ No newline at end of file diff --git a/java8-datetime-api/README.md b/java8-datetime-api/README.md new file mode 100644 index 0000000..0ab385f --- /dev/null +++ b/java8-datetime-api/README.md @@ -0,0 +1,623 @@ +# Java 8 新的日期时间 API + +## ZoneId + +Java 8中的时区操作被很大程度上简化了,新的时区类 `java.time.ZoneId` 是原有的 `java.util.TimeZone` 类的替代品。 +ZoneId对象可以通过 `ZoneId.of()` 方法创建,也可以通过 `ZoneId.systemDefault()` 获取系统默认时区: + +```java +ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai"); +ZoneId systemZoneId = ZoneId.systemDefault(); +``` + +`of()` 方法接收一个“区域/城市”的字符串作为参数,你可以通过 `getAvailableZoneIds()` 方法获取所有合法的“区域/城市”字符串: + +```java +Set zoneIds = ZoneId.getAvailableZoneIds(); +``` + +对于老的时区类 `TimeZone`,Java 8也提供了转化方法: + +```java +ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId(); +``` + +有了 `ZoneId`,我们就可以将一个 `LocalDate`、`LocalTime` 或 `LocalDateTime` 对象转化为 `ZonedDateTime` 对象: + +```java +LocalDateTime localDateTime = LocalDateTime.now(); +ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId); +``` + +`ZonedDateTime` 对象由两部分构成,`LocalDateTime` 和 `ZoneId`,其中 `2018-03-03T15:26:56.147` 部分为 `LocalDateTime`,`+08:00[Asia/Shanghai]` 部分为ZoneId。 + +另一种表示时区的方式是使用 `ZoneOffset`,它是以当前时间和 **世界标准时间(UTC)/格林威治时间(GMT)** 的偏差来计算,例如: + +```java +ZoneOffset zoneOffset = ZoneOffset.of("+09:00"); +LocalDateTime localDateTime = LocalDateTime.now(); +OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset); +``` + +## Instant + +Instant类在Java日期与时间功能中,表示了时间线上一个确切的点,定义为距离初始时间的时间差(初始时间为GMT 1970年1月1日00:00)经测量一天有86400秒,从初始时间开始不断向前移动。 + +**创建一个Instant实例** + +你可以通过Instant类的工厂方法创建一个Instant实例,例如你可以调用instant.now()来创建一个确切的表达当前时间的Instant对象: + +```java +Instant now = Instant.now(); +``` + +另外也有一些其它方法能创建Instant,具体请查阅Java官方文档。 + +**访问Instant的时间** + +一个Instant对象里有两个域:距离初始时间的秒钟数、在当前一秒内的第几纳秒,他们的组合表达了当前时间点。你可以通过以下两个方法得到它们的值: + +```java +long seconds = getEpochSecond() +int nanos = getNano() +``` + +**Instant的计算** + +Instant类有一些方法,可以用于获得另一Instant的值,例如: + +- `plusSeconds()` +- `plusMillis()` +- `plusNanos()` +- `minusSeconds()` +- `minusMillis()` +- `minusNanos()` + +我下面将向你展示两个例子,来说明这些方法如何使用: + +```java +Instant now = Instant.now(); +Instant later = now.plusSeconds(3); +Instant earlier = now.minusSeconds(3); +``` + +第一行获得了一个Instant对象,表示当前时间。第二行创建了一个Instant表示三秒后,第三行创建了一个Instant表示三秒前。 + +> seconds 表示从 `1970-01-01 00:00:00` 开始到现在的秒数,nanos 表示纳秒部分(nanos的值不会超过999,999,999) + +## Clock + +Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 `System.currentTimeMillis()` 来获取当前的微秒数。 +某一个特定的时间点也可以使用Instant类来表示,Instant 类也可以用来创建老的 `java.util.Date` 对象。 + +```java +Clock clock = Clock.systemDefaultZone(); +long millis = clock.millis(); +Instant instant = clock.instant(); +Date legacyDate = Date.from(instant); // legacy java.util.Date +``` + +## LocalDate + +LocalDate类是Java 8中日期时间功能里表示一个本地日期的类,它的日期是无时区属性的。 +可以用来表示生日、节假日期等等。这个类用于表示一个确切的日期,而不是这个日期所在的时间(如java.util.Date中的2000.01.01表示的实际是这一天的00:00这个瞬间)。 + +LocalDate类位于java.time包下,人名叫java.time.LocalDate,创建出来的实例也是不可变对象,所以涉及它的计算方法将返回一个新的LocalDate。 + +**创建一个LocalDate实例** + +我们有多种方式可以创建出 `LocalDate` 实例。第一种方法是使用 `now()` 方法获得值为今天当日的 `LocalDate` 对象: + +```java +LocalDate localDate = LocalDate.now(); +``` + +另一种方法是使用年月日信息构造出LocalDate对象: + +```java +LocalDate localDate2 = LocalDate.of(2018, 3, 3); +``` + +LocalDate 的 `of()` 方法创建出一个指定年月日的日期,并且没有时区信息。 + +**访问日期信息** + +可以用如下方法访问LocalDate中的日期信息: + +```java +int year = localDate.getYear(); +Month month = localDate.getMonth(); +int dayOfMonth = localDate.getDayOfMonth(); +int dayOfYear = localDate.getDayOfYear(); +DayOfWeek dayOfWeek = localDate.getDayOfWeek(); +``` + +可以注意到getMonth()与getDayOfWeek()方法返回了一个枚举类型代替一个int。你可以通过枚举类型中的getValue()来获得信息。 + +**LocalDate计算** + +你可以进行一堆简单的日期计算,只要使用如下的方法: + +- `plusDays()` +- `plusWeeks()` +- `plusMonths()` +- `plusYears()` +- `minusDays()` +- `minusWeeks()` +- `minusMonths()` +- `minusYears()` + +以下举几个使用的例子来帮助理解使用: + +```java +LocalDate d = LocalDate.of(2018, 3, 5); +LocalDate d1 = localDate.plusYears(3); +LocalDate d2 = localDate.minusYears(3); +``` + +1. 第一行创建出一个新的LocalDate对象d,表示2018.3.5。 +2. 第二行创建了值等于d日期3年后的LocalDate对象,第三行也是一样,只是值改为d日期的三年前。 + +## LocalTime + +LocalTime类是Java 8中日期时间功能里表示一整天中某个时间点的类,它的时间是无时区属性的(早上10点等等)。比如你需要描述学校几点开学,这个时间不涉及在什么城市,这个描述是对任何国家城市都适用的,此时使用无时区的LocalTime就足够了。 +LocalTime类的对象也是不可变的,所以计算方法会返回一个新的LocalTime实例。 + +**创建一个LocatTime实例** + +有多种方式可以新建LocalTime实例。比如使用当前时间作为值新建对象: + +```java +LocalTime localTime = LocalTime.now(); +``` + +另一种方式是使用指定的时分秒和纳秒来新建对象: + +```java +LocalTime localTime2 = LocalTime.of(21, 30, 59, 11001); +``` + +也有另一种版本的 `of()` 方法只需要小时分钟两项,或时分秒三项值作为参数。 + +**访问LocalTime对象的时间** + +你可以通过这些方法访问其时、分、秒、纳秒: + +- `getHour()` +- `getMinute()` +- `getSecond()` +- `getNano()` + +**LocalTime的计算** + +LocalTime类包含一系列方法,能帮你完成时间计算: + +- `plusHours()` +- `plusMinutes()` +- `plusSeconds()` +- `plusNanos()` +- `minusHours()` +- `minusMinutes()` +- `minusSeconds()` +- `minusNanos()` + +以下举一个例子: + +```java +LocalTime localTime2 = LocalTime.of(21, 30, 59, 11001); +LocalTime localTimeLater = localTime.plusHours(3); +LocalTime localTimeEarlier = localTime.minusHours(3); +``` + +1. 第一行新建一个LocalTime实例,表示21:30:50的第11001纳秒。 +2. 第二行新建了一个LocalTime实例表示这个时间的三小时后,第三行表示三小时前。 +3. LocalTime类是Java 8中日期时间功能里表示一整天中某个时间点的类,它的时间是无时区属性的(早上10点等等)。比如你需要描述学校几点开学,这个时间不涉及在什么城市,这个描述是对任何国家城市都适用的,此时使用无时区的LocalTime就足够了。 + +LocalTime类的对象也是不可变的,所以计算方法会返回一个新的LocalTime实例。 + +## LocalDateTime + +LocalDateTime类是Java 8中日期时间功能里,用于表示当地的日期与时间的类,它的值是无时区属性的。你可以将其视为Java 8中LocalDate与LocalTime两个类的结合。 + +LocalDateTime类的值是不可变的,所以其计算方法会返回一个新的LocalDateTime实例。 + +**创建一个LocatDateTime实例** + +可以通过LocalDateTime的静态工厂方法来创建LocalDateTime实例。以下举例使用 `now()` 方法创建: + +```java +LocalDateTime localDateTime = LocalDateTime.now(); +``` + +另一种方式是使用指定的年月日、时分秒、纳秒来新建对象: + +```java +LocalDateTime localDateTime2 = LocalDateTime.of(2018, 11, 26, 13, 55, 36, 123); +``` + +**访问LocalDateTime对象的时间** + +你可以通过这些方法访问其日期时间: + +- `getYear()` +- `getMonth()` +- `getDayOfMonth()` +- `getDayOfWeek()` +- `getDayOfYear()` +- `getHour()` +- `getMinute()` +- `getSecond()` +- `getNano()` + +这些方法中有一些返回int有一些返回枚举类型,你可以通过枚举类型中的 `getValue()` 方法来获得int值。 + +**LocalDateTime的计算** + +LocalDateTime 类包含一系列方法,能帮你完成时间计算: + +- `plusYears()` +- `plusMonths()` +- `plusDays()` +- `plusHours()` +- `plusMinutes()` +- `plusSeconds()` +- `plusNanos()` +- `minusYears()` +- `minusMonths()` +- `minusDays()` +- `minusHours()` +- `minusMinutes()` +- `minusSeconds()` +- `minusNanos()` + +以下举一个例子: + +```java +LocalDateTime localDateTime = LocalDateTime.now(); +LocalDateTime localDateTime1 = localDateTime.plusYears(3); +LocalDateTime localDateTime2 = localDateTime.minusYears(3); +``` + +1. 第一行新建一个LocalDateTime实例表示当前这个时间。 +2. 第二行新建了一个LocalDateTime实例表示三年后。 +3. 第三行也新建了一个LocalDateTime实例表示三小时前。 + +## ZonedDateTime + +ZonedDateTime类是Java 8中日期时间功能里,用于表示带时区的日期与时间信息的类。可以用于表示一个真实事件的开始时间,如某火箭升空时间等等。 + +ZonedDateTime 类的值是不可变的,所以其计算方法会返回一个新的ZonedDateTime 实例。 + +**创建一个ZonedDateTime实例** + +有多种方式可以新建ZonedDateTime实例。比如使用当前时间作为值新建对象: + +```java +ZonedDateTime dateTime = ZonedDateTime.now(); +``` + +另一种方式是使用指定的年月日、时分秒、纳秒以及时区ID来新建对象: + +```java +ZoneId zoneId = ZoneId.of("UTC+1"); +ZonedDateTime dateTime2 = ZonedDateTime.of(2015, 11, 30, 23, 45, 59, 1234, zoneId); +``` + +**访问ZonedDateTime对象的时间** + +你可以通过这些方法访问其日期时间: + +- `getYear()` +- `getMonth()` +- `getDayOfMonth()` +- `getDayOfWeek()` +- `getDayOfYear()` +- `getHour()` +- `getMinute()` +- `getSecond()` +- `getNano()` + +这些方法中有一些返回int有一些返回枚举类型,但可以通过枚举类型中的getValue()方法来获得int值。 + +**ZonedDateTime的计算** + +ZonedDateTime类包含一系列方法,能帮你完成时间计算: + +- `plusYears()` +- `plusMonths()` +- `plusDays()` +- `plusHours()` +- `plusMinutes()` +- `plusSeconds()` +- `plusNanos()` +- `minusYears()` +- `minusMonths()` +- `minusDays()` +- `minusHours()` +- `minusMinutes()` +- `minusSeconds()` +- `minusNanos()` + +但注意计算时,若不巧跨越了夏令时(会补一小时或减一小时),可能得不到希望的结果。一个替代的正确做法是使用Period: + +```java +ZonedDateTime zoneDateTime = previousDateTime.plus(Period.ofDays(3)); +``` + +**时区** + +时区是用ZoneId类表示的,你可以使用ZoneId.now()或ZoneId.of(“xxx”)来实例化: + +```java +ZoneId zoneId = ZoneId.of("UTC+1"); +``` + +传给 `of()` 方法的参数是时区的ID,如“UTC+1”指距离UTC(格林威治时间)有一小时的时差,你可以使用你想要的时差来表示ZoneId(如+1与-5等等) +你也可以使用另一种方式表示zone id,即使用地区名字,也是可以的: + +```java +ZoneId zoneId2 = ZoneId.of("Europe/Copenhagen"); +ZoneId zoneId3 = ZoneId.of("Europe/Paris"); +``` + +## DateTimeFormatter + +DateTimeFormatter类是Java 8中日期时间功能里,用于解析和格式化日期时间的类,位于 `java.time.format` 包下。 + +**预定义的DateTimeFormatter实例** + +DateTimeFormatter类包含一系列预定义(常量)的实例,可以解析和格式化一些标准时间格式。这将让你免除麻烦的时间格式定义,类中包含如下预定义的实例: + +```java +BASIC_ISO_DATE + +ISO_LOCAL_DATE +ISO_LOCAL_TIME +ISO_LOCAL_DATE_TIME + +ISO_OFFSET_DATE +ISO_OFFSET_TIME +ISO_OFFSET_DATE_TIME + +ISO_ZONED_DATE_TIME + +ISO_INSTANT + +ISO_DATE +ISO_TIME +ISO_DATE_TIME + +ISO_ORDINAL_TIME +ISO_WEEK_DATE + +RFC_1123_DATE_TIME +``` + +每个预定义的DateTimeFormatter实例都有不同的日期格式,我就不解释全部的了。具体的可以查阅Java官方文档,但我在这篇的后续中会解释其中几个,以方便理解。 + +**格式化日期** + +当你获取一个DateTimeFormatter实例后,就可以用format()方便来将一个日期格式化为某种字符串,例如: + +```java +DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE; +String formattedDate = formatter.format(LocalDate.now()); +System.out.println(formattedDate); +``` + +这个样例把LocalDate对象格式化了,并输出20150703,这个输出表示现在2018年,3月5日。 +再举一个关于ZonedDateTime的例子: + +```java +DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE; +String formattedZonedDate = formatter.format(ZonedDateTime.now()); +System.out.println("formattedZonedDate = " + formattedZonedDate); +``` + +这个例子会输出:20180305+0800 +表示今年2018年,3月5日,位于UTC+8时区。 + +## Duration + +一个Duration对象表示两个Instant间的一段时间,是在Java 8中加入的新功能。 + +一个Duration实例是不可变的,当创建出对象后就不能改变它的值了。你只能通过Duration的计算方法,来创建出一个新的Durtaion对象。你会在之后的教程中见到的。 + +**创建Duration实例** + +使用 `Duration` 类的工厂方法来创建一个 `Duration` 对象,以下是一个使用 `between()` 的例子: + +```java +Instant first = Instant.now(); +// wait some time while something happens +Instant second = Instant.now(); +Duration duration = Duration.between(first, second); +``` + +**访问Duration的时间** + +一个Duration对象里有两个域:纳秒值(小于一秒的部分),秒钟值(一共有几秒),他们的组合表达了时间长度。注意屯使用System.getCurrentTimeMillis()时不同,Duration不包含毫秒这个属性。 +你可以通过以下两个方法得到它们的值: + +```java +long seconds = getSeconds() +int nanos = getNano() +``` + +你也可以转换整个时间到其它单位如纳秒、分钟、小时、天: + +- `toNanos()` +- `toMillis()` +- `toMinutes()` +- `toHours()` +- `toDays()` + +举例而言:`toNanos()` 与 `getNano()` 不同,`toNanos()` 获得的是 `Duration` 整个时间共有多少纳秒, +而 `getNano()` 只是获得这段时间中小于一秒的部分。 + +你也许会问,为什么没有 `toSeconds()` 方法,因为已经有 `getSeconds()` 这个方法能达到同样的功能了。 + +**Duration计算** + +Duration类包含一系列的计算方法: + +- `plusNanos()` +- `plusMillis()` +- `plusSeconds()` +- `plusMinutes()` +- `plusHours()` +- `plusDays()` +- `minusNanos()` +- `minusMillis()` +- `minusSeconds()` +- `minusMinutes()` +- `minusHours()` +- `minusDays()` + +这些方法所做的事都是相似的,我在这儿也不展示内部实现细节了,就展示一个加减的例子吧: + +```java +Duration start = ... //obtain a start duration +Duration added = start.plusDays(3); +Duration subtracted = start.minusDays(3); +``` + +1. 第一行创建了一个Duration对象叫start,具体怎么创建可以参考前面的代码。 +2. 第二三行样例创建了两个新的Duration,通过调用start的加减操作,使得added对象表示的时间比start多三天,而substracted则少三天。 + +所有的计算方法都会返回一个新的Duration,以保证Duration的不可变属性。 + +```java +long days = duration.toDays(); // 这段时间的总天数 +long hours = duration.toHours(); // 这段时间的小时数 +long minutes = duration.toMinutes(); // 这段时间的分钟数 +long seconds = duration.getSeconds(); // 这段时间的秒数 +long milliSeconds = duration.toMillis(); // 这段时间的毫秒数 +long nanoSeconds = duration.toNanos(); // 这段时间的纳秒数 +``` + +## 其他操作 + +### 增加和减少日期 + +Java 8中的日期/时间类都是不可变的,这是为了保证线程安全。当然,新的日期/时间类也提供了方法用于创建对象的可变版本,比如增加一天或者减少一天: + +```java +LocalDate date = LocalDate.of(2017, 1, 5); // 2017-01-05 + +LocalDate date1 = date.withYear(2016); // 修改为 2016-01-05 +LocalDate date2 = date.withMonth(2); // 修改为 2017-02-05 +LocalDate date3 = date.withDayOfMonth(1); // 修改为 2017-01-01 + +LocalDate date4 = date.plusYears(1); // 增加一年 2018-01-05 +LocalDate date5 = date.minusMonths(2); // 减少两个月 2016-11-05 +LocalDate date6 = date.plus(5, ChronoUnit.DAYS); // 增加5天 2017-01-10 +``` + +上面例子中对于日期的操作比较简单,但是有些时候我们要面临更复杂的时间操作,比如将时间调到下一个工作日, +或者是下个月的最后一天,这时候我们可以使用 `with()` 方法的另一个重载方法,它接收一个TemporalAdjuster参数, +可以使我们更加灵活的调整日期: + +```java +LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 返回下一个距离当前时间最近的星期日 +LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY)); // 返回本月最后一个星期六 +``` + +要使上面的代码正确编译,你需要使用静态导入 `TemporalAdjusters` 对象: + +`import static java.time.temporal.TemporalAdjusters.*;` + +`TemporalAdjusters` 类中包含了很多静态方法可以直接使用,下面的表格列出了一些方法: + +| 方法名 | 描述 | +|:-----:|:-------| +| `dayOfWeekInMonth` | 返回同一个月中每周的第几天 | +| `firstDayOfMonth` | 返回当月的第一天 | +| `firstDayOfNextMonth` | 返回下月的第一天 | +| `firstDayOfNextYear` | 返回下一年的第一天 | +| `firstDayOfYear` | 返回本年的第一天 | +| `firstInMonth` | 返回同一个月中第一个星期几 | +| `lastDayOfMonth` | 返回当月的最后一天 | +| `lastDayOfNextMonth` | 返回下月的最后一天 | +| `lastDayOfNextYear` | 返回下一年的最后一天 | +| `lastDayOfYear` | 返回本年的最后一天 | +| `lastInMonth` | 返回同一个月中最后一个星期几 | +| `next / previous` | 返回后一个/前一个给定的星期几 | +| `nextOrSame / previousOrSame` | 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 | + +如果上面表格中列出的方法不能满足你的需求,你还可以创建自定义的 `TemporalAdjuster` 接口的实现, +`TemporalAdjuster` 也是一个函数式接口,所以我们可以使用Lambda表达式: + +```java +@FunctionalInterface +public interface TemporalAdjuster { + Temporal adjustInto(Temporal temporal); +} +``` + +比如给定一个日期,计算该日期的下一个工作日(不包括星期六和星期天): + +```java +LocalDate date = LocalDate.of(2017, 1, 5); +date.with(temporal -> { + // 当前日期 + DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + + // 正常情况下,每次增加一天 + int dayToAdd = 1; + + // 如果是星期五,增加三天 + if (dayOfWeek == DayOfWeek.FRIDAY) { + dayToAdd = 3; + } + + // 如果是星期六,增加两天 + if (dayOfWeek == DayOfWeek.SATURDAY) { + dayToAdd = 2; + } + + return temporal.plus(dayToAdd, ChronoUnit.DAYS); +}); +``` + +### 其他历法 + +Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当做公元0年,以此类推。 + + +此外Java 8还提供了4套其他历法(很奇怪为什么没有汉族人使用的农历),每套历法都包含一个日期类,分别是: + +- `ThaiBuddhistDate`:泰国佛教历 +- `MinguoDate`:中华民国历 +- `JapaneseDate`:日本历 +- `HijrahDate`:伊斯兰历 + +每个日期类都继承 `ChronoLocalDate` 类,所以可以在不知道具体历法的情况下也可以操作。不过这些历法一般不常用,除非是有某些特殊需求情况下才会使用。 + +这些不同的历法也可以用于向公历转换: + +```java +LocalDate date = LocalDate.now(); +JapaneseDate jpDate = JapaneseDate.from(date); +``` + +由于它们都继承ChronoLocalDate类,所以在不知道具体历法情况下,可以通过ChronoLocalDate类操作日期: + +```java +Chronology jpChronology = Chronology.ofLocale(Locale.JAPANESE); +ChronoLocalDate jpChronoLocalDate = jpChronology.dateNow(); +``` + +我们在开发过程中应该尽量避免使用 `ChronoLocalDate`,尽量用与历法无关的方式操作时间,因为不同的历法计算日期的方式不一样, +比如开发者会在程序中做一些假设,假设一年中有12个月,如果是中国农历中包含了闰月,一年有可能是13个月, +但开发者认为是12个月,多出来的一个月属于明年的。 +再比如假设年份是累加的,过了一年就在原来的年份上加一,但日本天皇在换代之后需要重新纪年,所以过了一年年份可能会从1开始计算。 + +在实际开发过程中建议使用 `LocalDate`,包括存储、操作、业务规则的解读;除非需要将程序的输入或者输出本地化, +这时可以使用 `ChronoLocalDate` 类。 + +### 资料 + +- [SimpleDateFormat的线程安全问题与解决方案](http://www.cnblogs.com/zemliu/p/3290585.html) +- [为什么SimpleDateFormat不是线程安全的?](http://blog.csdn.net/yiifaa/article/details/73499053) +- [Java获取N天前,N天后的日期(如3天)](http://blog.csdn.net/liuwei0376/article/details/13620879) diff --git a/java8-datetime-api/pom.xml b/java8-datetime-api/pom.xml index 9bb6c63..fcc8c7e 100644 --- a/java8-datetime-api/pom.xml +++ b/java8-datetime-api/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ClockExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ClockExample.java new file mode 100644 index 0000000..52f87de --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ClockExample.java @@ -0,0 +1,23 @@ +package io.github.biezhi.datetime; + +import java.time.Clock; +import java.time.Instant; +import java.util.Date; + +/** + * Clock 示例 + * + * @author biezhi + * @date 2018/3/3 + */ +public class ClockExample { + + public static void main(String[] args) { + Clock clock = Clock.systemDefaultZone(); + long millis = clock.millis(); + Instant instant = clock.instant(); + Date legacyDate = Date.from(instant); // legacy java.util.Date + System.out.println(millis); + System.out.println(legacyDate); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ConvertExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ConvertExample.java new file mode 100644 index 0000000..0e51f24 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ConvertExample.java @@ -0,0 +1,91 @@ +package io.github.biezhi.datetime; + +import java.time.*; +import java.util.Date; + +/** + * 日期转换示例 + * + * @author biezhi + * @date 2018/3/3 + */ +public class ConvertExample { + + /** + * LocalDate -> Date + * + * @param localDate + * @return + */ + public static Date toDate(LocalDate localDate) { + ZoneId zone = ZoneId.systemDefault(); + Instant instant = localDate.atStartOfDay().atZone(zone).toInstant(); + return Date.from(instant); + } + + /** + * LocalDateTime -> Date + * + * @param localDateTime + * @return + */ + public static Date toDate(LocalDateTime localDateTime) { + ZoneId zone = ZoneId.systemDefault(); + Instant instant = localDateTime.atZone(zone).toInstant(); + return Date.from(instant); + } + + /** + * LocalTime -> Date + * + * @param localTime + * @return + */ + public static Date toDate(LocalTime localTime) { + LocalDate localDate = LocalDate.now(); + LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + ZoneId zone = ZoneId.systemDefault(); + Instant instant = localDateTime.atZone(zone).toInstant(); + return Date.from(instant); + } + + /** + * Date -> LocalDate + * + * @param date + * @return + */ + public static LocalDate toLocalDate(Date date) { + Instant instant = date.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); + return localDateTime.toLocalDate(); + } + + /** + * Date -> LocalDateTime + * + * @param date + * @return + */ + public static LocalDateTime toLocalDateTime(Date date) { + Instant instant = date.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + return LocalDateTime.ofInstant(instant, zone); + } + + /** + * Date -> LocalTime + * + * @param date + * @return + */ + public static LocalTime toLocalTime(Date date) { + Instant instant = date.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); + return localDateTime.toLocalTime(); + } + + +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DateTimeFormatExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DateTimeFormatExample.java new file mode 100644 index 0000000..84ab198 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DateTimeFormatExample.java @@ -0,0 +1,49 @@ +package io.github.biezhi.datetime; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * DateTimeFormatter 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class DateTimeFormatExample { + + public static void main(String[] args) { + DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE; + String formattedDate = formatter.format(LocalDate.now()); + String formattedZonedDate = formatter.format(ZonedDateTime.now()); + + System.out.println("LocalDate : " + formattedDate); + System.out.println("formattedZonedDate : " + formattedZonedDate); + + LocalDateTime dateTime = LocalDateTime.now(); + String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE); // 20180303 + String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2013-03-03 + String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME); // 当前时间 + String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 2018-03-03 + // 今天是:2018年 三月 03日 星期六 + String strDate5 = dateTime.format(DateTimeFormatter.ofPattern("今天是:YYYY年 MMMM dd日 E", Locale.CHINESE)); + + System.out.println(strDate1); + System.out.println(strDate2); + System.out.println(strDate3); + System.out.println(strDate4); + System.out.println(strDate5); + + // 将一个字符串解析成一个日期对象 + String strDate6 = "2018-03-03"; + String strDate7 = "2017-03-03 15:30:05"; + + LocalDate date = LocalDate.parse(strDate6, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + LocalDateTime dateTime1 = LocalDateTime.parse(strDate7, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + + System.out.println(date); + System.out.println(dateTime1); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DurationExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DurationExample.java new file mode 100644 index 0000000..2a0aaca --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/DurationExample.java @@ -0,0 +1,34 @@ +package io.github.biezhi.datetime; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; + +/** + * Duration 示例 + * + * @author biezhi + * @date 2018/3/3 + */ +public class DurationExample { + + public static void main(String[] args) throws InterruptedException { + + // 创建Duration实例 + Instant first = Instant.now(); + Thread.sleep(3000); + Instant second = Instant.now(); + Duration duration = Duration.between(first, second); + + // 访问Duration的时间 + long seconds = duration.getSeconds(); + + System.out.println("相差 : " + seconds + " 秒"); + + LocalDateTime from = LocalDateTime.now(); + LocalDateTime to = from.plusDays(5); + Duration duration2 = Duration.between(from, to); + + System.out.println("从 from 到 to 相差 : " + duration2.toDays() + " 天"); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/InstantExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/InstantExample.java new file mode 100644 index 0000000..05d9db7 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/InstantExample.java @@ -0,0 +1,33 @@ +package io.github.biezhi.datetime; + +import java.time.Instant; + +/** + * Instant 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class InstantExample { + + public static void main(String[] args) { + + // 创建一个Instant实例 + Instant now = Instant.now(); + + // 访问Instant的时间 + long seconds = now.getEpochSecond(); + int nanos = now.getNano(); + System.out.println("seconds : " + seconds); + System.out.println("nanos : " + nanos); + + // 3秒后 + Instant later = now.plusSeconds(3); + // 3秒前 + Instant earlier = now.minusSeconds(3); + + System.out.println("current : " + now.toString()); + System.out.println("later : " + later); + System.out.println("earlier : " + earlier); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateExample.java new file mode 100644 index 0000000..cac4935 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateExample.java @@ -0,0 +1,42 @@ +package io.github.biezhi.datetime; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.Month; + +/** + * LocalDate 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class LocalDateExample { + + public static void main(String[] args) { + + // 创建一个LocalDate实例 + LocalDate localDate = LocalDate.now(); + + // 使用年月日信息构造出LocalDate对象 + LocalDate localDate2 = LocalDate.of(2018, 3, 3); + + int year = localDate.getYear(); + Month month = localDate.getMonth(); + int dayOfMonth = localDate.getDayOfMonth(); + int dayOfYear = localDate.getDayOfYear(); + DayOfWeek dayOfWeek = localDate.getDayOfWeek(); + + System.out.println("year : " + year); + System.out.println("month : " + month.getValue()); + System.out.println("dayOfMonth : " + dayOfMonth); + System.out.println("dayOfYear : " + dayOfYear); + System.out.println("dayOfWeek : " + dayOfWeek.getValue()); + + // 3年后 + LocalDate d1 = localDate2.plusYears(3); + // 3年前 + LocalDate d2 = localDate2.minusYears(3); + System.out.println("plusYears : " + d1); + System.out.println("minusYears : " + d2); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateTimeExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateTimeExample.java new file mode 100644 index 0000000..b6714f7 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalDateTimeExample.java @@ -0,0 +1,31 @@ +package io.github.biezhi.datetime; + +import java.time.LocalDateTime; + +/** + * LocalDateTime 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class LocalDateTimeExample { + + public static void main(String[] args) { + + // 创建一个LocalDateTime实例 + LocalDateTime localDateTime = LocalDateTime.now(); + + // 使用指定的年月日、时分秒、纳秒来新建对象 + LocalDateTime localDateTime2 = LocalDateTime.of(2018, 11, 26, 13, 55, 36, 123); + + // 3年后的现在 + LocalDateTime dt1 = localDateTime.plusYears(3); + // 3年前的现在 + LocalDateTime dt2 = localDateTime.minusYears(3); + + System.out.println("localDateTime : " + localDateTime); + System.out.println("localDateTime2 : " + localDateTime2); + System.out.println("dt1 : " + dt1); + System.out.println("dt2 : " + dt2); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalTimeExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalTimeExample.java new file mode 100644 index 0000000..f226a80 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/LocalTimeExample.java @@ -0,0 +1,34 @@ +package io.github.biezhi.datetime; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; + +/** + * LocalTime 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class LocalTimeExample { + + public static void main(String[] args) { + + // 创建一个LocalTime实例 + LocalTime localTime = LocalTime.now(); + + // 使用指定的时分秒和纳秒来新建对象 + LocalTime localTime2 = LocalTime.of(21, 30, 59, 11001); + + // 3小时后 + LocalTime localTimeLater = localTime.plusHours(3); + // 3小时前 + LocalTime localTimeEarlier = localTime.minusHours(3); + + System.out.println("localTime : " + localTime); + System.out.println("localTime2 : " + localTime2); + System.out.println("localTimeLater : " + localTimeLater); + System.out.println("localTimeEarlier: " + localTimeEarlier); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZoneIdExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZoneIdExample.java new file mode 100644 index 0000000..8041be1 --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZoneIdExample.java @@ -0,0 +1,29 @@ +package io.github.biezhi.datetime; + +import java.time.ZoneId; +import java.util.TimeZone; + +/** + * ZoneId 示例 + * + * @author biezhi + * @date 2018/3/3 + */ +public class ZoneIdExample { + + public static void main(String[] args) { + + // 获取系统默认时区 + ZoneId defaultZoneId = ZoneId.systemDefault(); + ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai"); + + // TimeZone 转换为 ZoneId + ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId(); + + System.out.println(defaultZoneId); + System.out.println(shanghaiZoneId); + System.out.println(oldToNewZoneId); + + System.out.println(ZoneId.getAvailableZoneIds()); + } +} diff --git a/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZonedDateTimeExample.java b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZonedDateTimeExample.java new file mode 100644 index 0000000..d61e8fe --- /dev/null +++ b/java8-datetime-api/src/main/java/io/github/biezhi/datetime/ZonedDateTimeExample.java @@ -0,0 +1,34 @@ +package io.github.biezhi.datetime; + +import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +/** + * ZonedDateTime 示例 + * + * @author biezhi + * @date 2018/3/2 + */ +public class ZonedDateTimeExample { + + public static void main(String[] args) { + // 创建一个ZonedDateTime实例 + ZonedDateTime dateTime = ZonedDateTime.now(); + + // 使用指定的年月日、时分秒、纳秒以及时区ID来新建对象 + ZoneId zoneId = ZoneId.of("UTC+8"); + ZonedDateTime dateTime2 = ZonedDateTime.of(2018, 3, 8, 23, 45, 59, 1234, zoneId); + + // 3天后 + ZonedDateTime zoneDateTime = dateTime2.plus(Period.ofDays(3)); + + ZoneId zoneId2 = ZoneId.of("Europe/Copenhagen"); + ZoneId zoneId3 = ZoneId.of("Europe/Paris"); + + System.out.println("dateTime : " + dateTime); + System.out.println("zoneDateTime : " + zoneDateTime); + System.out.println("zoneId2 : " + zoneId2); + System.out.println("zoneId3 : " + zoneId3); + } +} diff --git a/java8-default-methods/README.md b/java8-default-methods/README.md new file mode 100644 index 0000000..f546e01 --- /dev/null +++ b/java8-default-methods/README.md @@ -0,0 +1,2 @@ +# Java 8 默认方法和静态方法 + diff --git a/java8-default-methods/pom.xml b/java8-default-methods/pom.xml index e67e426..252318a 100644 --- a/java8-default-methods/pom.xml +++ b/java8-default-methods/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/BasicCalculator.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/BasicCalculator.java new file mode 100644 index 0000000..6aa427a --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/BasicCalculator.java @@ -0,0 +1,27 @@ +package io.github.biezhi.java8.defaultmethods; + +public class BasicCalculator implements Calculator { + + @Override + public int add(int first, int second) { + return first + second; + } + + @Override + public int subtract(int first, int second) { + return first - second; + } + + @Override + public int divide(int number, int divisor) { + if (divisor == 0) { + throw new IllegalArgumentException("divisor can't be zero."); + } + return number / divisor; + } + + @Override + public int multiply(int first, int second) { + return first * second; + } +} \ No newline at end of file diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Calculator.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Calculator.java new file mode 100644 index 0000000..595339b --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Calculator.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.defaultmethods; + +/** + * 计算器接口 + * + * @author biezhi + * @date 2018/2/11 + */ +public interface Calculator { + + int add(int first, int second); + + int subtract(int first, int second); + + int multiply(int first, int second); + + int divide(int number, int divisor); + + default int mod(int first, int second){ + return first % second; + } + + static Calculator getInstance(){ + return new BasicCalculator(); + } +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/CalculatorFactory.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/CalculatorFactory.java new file mode 100644 index 0000000..82486f8 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/CalculatorFactory.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.defaultmethods; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class CalculatorFactory { + + public static Calculator getInstance(){ + return new BasicCalculator(); + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Collection.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Collection.java new file mode 100644 index 0000000..37a7e0b --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/Collection.java @@ -0,0 +1,24 @@ +package io.github.biezhi.java8.defaultmethods; + +import java.util.Iterator; +import java.util.function.Predicate; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public interface Collection extends java.util.Collection { + + default boolean removeIf2(Predicate predicate){ + boolean isRemoved = false; + Iterator iterator = iterator(); + while(iterator.hasNext()){ + if(predicate.test(iterator.next())){ + iterator.remove(); + isRemoved = true; + } + } + return isRemoved; + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/UseCalc.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/UseCalc.java new file mode 100644 index 0000000..75a5d13 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/UseCalc.java @@ -0,0 +1,21 @@ +package io.github.biezhi.java8.defaultmethods; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class UseCalc { + + public static void main(String[] args) { + + Calculator calculator = new BasicCalculator(); + int sum = calculator.add(1, 2); + System.out.println(sum); + + Calculator cal = Calculator.getInstance(); + int difference = cal.subtract(3, 2); + System.out.println(difference); + + System.out.println(cal.mod(3, 2)); + } +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/A.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/A.java new file mode 100644 index 0000000..85d1f4c --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/A.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public interface A { + + default void sayHello(){ + System.out.println("你好,我是 Java 8"); + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App.java new file mode 100644 index 0000000..2f18440 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App.java @@ -0,0 +1,18 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class App implements A { + + @Override + public void sayHello(){ + System.out.println("你好,我是 APP"); + } + + public static void main(String[] args) { + new App().sayHello(); + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App2.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App2.java new file mode 100644 index 0000000..3e858d0 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App2.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class App2 implements A, B, C { + + public static void main(String[] args) { + new App2().sayHello(); + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App3.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App3.java new file mode 100644 index 0000000..f8f69a0 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/App3.java @@ -0,0 +1,19 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class App3 implements A, B { + + @Override + public void sayHello() { + System.out.println("大家好,我系古天乐。探晚懒月,里没有晚过的传奇。" + + "点一下,晚一连,撞贝不花一份钱。机要晚过了传骑,里就系我的凶第。"); + } + + public static void main(String[] args) { + new App3().sayHello(); + } + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/B.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/B.java new file mode 100644 index 0000000..b8a581a --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/B.java @@ -0,0 +1,9 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public interface B { + +} diff --git a/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/C.java b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/C.java new file mode 100644 index 0000000..af38f67 --- /dev/null +++ b/java8-default-methods/src/main/java/io/github/biezhi/java8/defaultmethods/conflict/C.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.defaultmethods.conflict; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public interface C extends A { + + default void sayHello(){ + System.out.println("你好,我是 渣渣辉"); + } + +} diff --git a/java8-growing/README.md b/java8-growing/README.md new file mode 100644 index 0000000..412f4fd --- /dev/null +++ b/java8-growing/README.md @@ -0,0 +1,354 @@ +# Java 8 的发展 + +## JDK 5 + +**自动装箱与拆箱** + +JDK1.5为每一个基本数据类型定义了一个封装类。使java中的基本数据类型也有自己的对象 + +```bash +int -->Integer +double --> Double +long --> Long +char --> Character +float --> Float +boolean --> Boolean +short --> Short +byte -- > Byte +``` + +- 自动装包:将基本类型转换成为对象,例如:`int --> Integer` +- 自动拆包:将对象转换成为基本数据类型,例如:`Integer --> int` + +对于 JDK1.5 之前集合总不能存放基本数据类型的问题,现在也能够解决。 + +**枚举** + +枚举是 JDK1.5 推出的一个比较重要的特性。其关键字为 `enum` +例如:定义代表交通灯的枚举 + +```java +public enum MyEnum{ + RED,GREEN,YELLOW +} +``` + +**静态导入** + +- 优点:使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名。 +- 缺点:过度使用会降低代码的可读性 + +**变长参数** + +在JDK1.5以前,当我们要为一个方法传递多个类型相同的参数时, +我们有两种方法解决 + +1. 直接传递一个数组过去 +2. 有多少个参数就传递多少个参数。 + +例如: + +```java +public void printColor(String red,String green,String yellow){ +} +``` + +或者 + +```java +public void printColor(String[] colors){ + +} +``` + +这样编写方法参数虽然能够实现我们想要的效果,但是,这样是不是有点麻烦呢? +再者,如果参数个数不确定,我们怎么办呢?Java JDK1.5为我们提供的可变参数就能够完美的解决这个问题. + +例如: + +```java +public void printColor(String... colors){ + +} +``` + +如果参数的类型相同,那么可以使用 `类型+三个点` ,后面跟一个参数名称的形式。 +这样的好处就是,只要参数类型相同,无论传递几个参数都没有限制 +注意:可变参数必须是参数列表的最后一项(该特性对对象和基本数据类型都适用) + +**泛型** + +```java +//给集合指定存入类型,上面这个集合在存入数据的时候必须存入String类型的数据,否则编译器会报错 +List strs = new ArrayList(); +``` + +“泛型” 意味着编写的代码可以被不同类型的对象所重用。 +可见泛型的提出是为了编写重用性更好的代码。 +泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 + +比如常见的集合类 `LinkedList`,其实现的接口名后有个特殊的部分 `<>`,而且它的成员的类型 Link 也包含一个 `<>`,这个符号的就是类型参数, +它使得在运行中,创建一个 LinkedList 时可以传入不同的类型,比如 `new LinkedList`,这样它的成员存放的类型也是 `String`。 + +**For-Each循环** + +例如上面这个集合我们可以通过for-each遍历,这样更加简单清晰 + +```java +for(String s : strs){ + System.out.println(s); +} +``` + +> 注意:使用for-each遍历集合时,要遍历的集合必须实现了Iterator接口 + +**线程并发库** + +线程并发库是 Java1.5 提出的关于多线程处理的高级功能,所在包:`java.util.concurrent` 包括 + +1. 线程互斥工具类:Lock,ReadWriteLock +2. 线程通信:Condition +3. 线程池:ExecutorService +3. 同步队列:ArrayBlockingQueue +4. 同步集合:ConcurrentHashMap,CopyOnWriteArrayList +5. 线程同步工具:Semaphore + +## JDK 6 + +**Desktop类和SystemTray类** + +前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件, +用默认应用程序打开或编辑文件(比如,用记事本打开以 txt 为后缀名的文件), +用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序。 + +**使用Compiler API** + +现在我们可以用JDK1.6 的Compiler API(JSR 199)去动态编译Java源文件, +Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。 + +这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后, +是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件。 +当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码, +这种方式需要我们产生另一个进程去做编译工作,不够优雅而且容易使代码依赖与特定的操作系统; +Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。 + +**轻量级Http Server API** + +JDK1.6 提供了一个简单的 Http Server API,据此我们可以构建自己的嵌入式 Http Server, +它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的 Http Server API来实现, +程序员必须自己实现 HttpHandler 接口,HttpServer 会调用 `HttpHandler` 实现类的回调方法来处理客户端请求, +在这里,我们把一个 Http 请求和它的响应称为一个交换,包装成 `HttpExchange` 类,`HttpServer` 负责将 `HttpExchange` 传给 `HttpHandler` 实现类的回调方法。 + +**用Console开发控制台程序** + +JDK1.6 中提供了 `java.io.Console` 类专用来访问基于字符的控制台设备。 +你的程序如果要与 Windows 下的 cmd 或者 Linux 下的 Terminal 交互,就可以用 `Console` 类代劳。 +但我们不总是能得到可用的 Console,一个JVM是否有可用的 Console 依赖于底层平台和 JVM 如何被调用。 +如果JVM是在交互式命令行(比如 Windows 的 cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的 Console 实例。 + +**对脚本语言的支持** + +如:ruby,groovy,javascript。 + +## JDK 7 + +**数字变量对下滑线的支持** + +JDK1.7可以在数值类型的变量里添加下滑线,但是有几个地方是不能添加的 + +1. 数字的开头和结尾 +2. 小数点前后 +3. F或者L前 + +例如: + +```java +int num = 1234_5678_9; +float num2 = 222_33F; +long num3 = 123_000_111L; +``` + +**switch对String的支持** + +```java +String status = "orderState"; +switch (status) { + case "ordercancel": + System.out.println("订单取消"); + break; + case "orderSuccess": + System.out.println("预订成功"); + break; + default: + System.out.println("状态未知"); +} +``` + +**try-with-resource** + +- `try-with-resources` 是一个定义了一个或多个资源的 try 声明,这个资源是指程序处理完它之后需要关闭它的对象。 +- `try-with-resources` 确保每一个资源在处理完成后都会被关闭。 + +可以使用try-with-resources的资源有: 任何实现了 `java.lang.AutoCloseable` 接口 `java.io.Closeable` 接口的对象。 + +例如: + +```java +public static String readFirstLineFromFile(String path) throws IOException { + + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + return br.readLine(); + } +} +``` + +在 java 7 以及以后的版本里,`BufferedReader` 实现了 `java.lang.AutoCloseable` 接口。 +由于 `BufferedReader` 定义在 `try-with-resources` 声明里,无论 `try` 语句正常还是异常的结束, +它都会自动的关掉。而在 java7 以前,你需要使用 `finally` 块来关掉这个对象。 + +**捕获多种异常并用改进后的类型检查来重新抛出异常** + +```java +public static void first(){ + try { + BufferedReader reader = new BufferedReader(new FileReader("")); + Connection con = null; + Statement stmt = con.createStatement(); + } catch (IOException | SQLException e) { + //捕获多个异常,e就是final类型的 + e.printStackTrace(); + } +} +``` + +优点:用一个 `catch` 处理多个异常,比用多个 `catch` 每个处理一个异常生成的字节码要更小更高效。 + +**创建泛型时类型推断** + +只要编译器可以从上下文中推断出类型参数,你就可以用一对空着的尖括号 `<>` 来代替泛型参数。 +这对括号私下被称为菱形(diamond)。 在Java SE 7之前,你声明泛型对象时要这样 + +```java +List list = new ArrayList(); +``` + +而在Java SE7以后,你可以这样 + +```java +List list = new ArrayList<>(); +``` + +因为编译器可以从前面(List)推断出推断出类型参数,所以后面的 `ArrayList` 之后可以不用写泛型参数了,只用一对空着的尖括号就行。 +当然,你必须带着菱形 `<>`,否则会有警告的。 +Java SE7 只支持有限的类型推断:只有构造器的参数化类型在上下文中被显著的声明了,你才可以使用类型推断,否则不行。 + +```java +List list = new ArrayList<>();l +list.add("A"); +//这个不行 +list.addAll(new ArrayList<>()); +// 这个可以 +List list2 = new ArrayList<>(); +list.addAll(list2); +``` + +## JDK 8 + +**Lambda表达式和函数式接口** + +Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法, +或者把代码本身当作数据处理:函数式开发者非常熟悉这些概念。很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持Lambda表达式,但是Java开发者没有选择,只能使用匿名内部类代替Lambda表达式。 +Lambda的设计耗费了很多时间和很大的社区力量,最终找到一种折中的实现方案,可以实现简洁而紧凑的语言结构。最简单的Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成。 + +Lambda的设计者们为了让现有的功能与Lambda表达式良好兼容,考虑了很多方法,于是产生了函数接口这个概念。函数接口指的是只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式。java.lang.Runnable和java.util.concurrent.Callable是函数式接口的最佳例子。在实践中,函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface(Java 库中的所有相关接口都已经带有这个注解了),举个简单的函数式接口的定义 + +**接口的默认方法和静态方法** + +Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。默认方法使得接口有点类似traits,不过要实现的目标不一样。默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。 +默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写 +由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。 +尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误。如果你想了解更多细节,可以参考官方文档。 + +**更好的类型推断** + +Java 8 编译器在类型推断方面有很大的提升,在很多场景下编译器可以推导出某个参数的数据类型,从而使得代码更为简洁。 + +参数 `Value.defaultValue()` 的类型由编译器推导得出,不需要显式指明。在Java 7中这段代码会有编译错误,除非使用 `Value.defaultValue()`。 + +**Optional** + +Java应用中最常见的bug就是空值异常。在Java 8之前,Google Guava引入了 `Optionals` 类来解决 `NullPointerException`, +从而避免源码被各种 `null` 检查污染,以便开发者写出更加整洁的代码。Java 8也将Optional加入了官方库。 +`Optional` 仅仅是一个容易存放T类型的值或者null。它提供了一些有用的接口来避免显式的null检查,可以参考Java 8官方文档了解更多细节。 + +如果Optional实例持有一个非空值,则 `isPresent()` 方法返回true,否则返回false;`orElseGet()` 方法,Optional实例持有null, +则可以接受一个lambda表达式生成的默认值;map()方法可以将现有的 `Optional` 实例的值转换成新的值;orElse()方法与orElseGet()方法类似, +但是在持有null的时候返回传入的默认值。 + +**Streams** + +新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。 +这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。 + +Task 类有一个分数(或伪复杂度)的概念,另外还有两种状态:OPEN 或者 CLOSED。现在假设有一个task集合, +首先看一个问题:在这个task集合中一共有多少个OPEN状态的点?在Java 8之前,要解决这个问题,则需要使用foreach循环遍历task集合; +但是在Java 8中可以利用steams解决:包括一系列元素的列表,并且支持顺序和并行处理。 + +```java +final Collection tasks = Arrays.asList( + new Task(Status.OPEN, 5), + new Task(Status.OPEN, 13), + new Task(Status.CLOSED, 8) +); + +// Calculate total points of all active tasks using sum() +final long totalPointsOfOpenTasks = tasks + .stream() + .filter(task -> task.getStatus() == Status.OPEN) + .mapToInt(Task::getPoints) + .sum(); + +System.out.println("Total points: " + totalPointsOfOpenTasks); +``` + +这里有很多知识点值得说。首先,tasks集合被转换成steam表示;其次,在steam上的filter操作会过滤掉所有CLOSED的task; +第三,mapToInt操作基于每个task实例的Task::getPoints方法将task流转换成Integer集合;最后,通过sum方法计算总和,得出最后的结果。 + +**新的日期时间 API** + +Java 8引入了新的Date-Time API(JSR 310)来改进时间、日期的处理。时间和日期的管理一直是最令Java开发者痛苦的问题。 +java.util.Date 和后来的 java.util.Calendar 一直没有解决这个问题(甚至令开发者更加迷茫)。 + +因为上面这些原因,诞生了第三方库Joda-Time,可以替代Java的时间管理API。 +Java 8中新的时间和日期管理API深受Joda-Time影响,并吸收了很多Joda-Time的精华。 +新的java.time包包含了所有关于日期、时间、时区、Instant(跟日期类似但是精确到纳秒)、duration(持续时间)和时钟操作的类。 +新设计的API认真考虑了这些类的不变性(从java.util.Calendar吸取的教训),如果某个实例需要修改,则返回一个新的对象。 + +第二,关注下LocalDate和LocalTime类。LocalDate仅仅包含ISO-8601日历系统中的日期部分;LocalTime则仅仅包含该日历系统中的时间部分。这两个类的对象都可以使用Clock对象构建得到。 +LocalDateTime类包含了LocalDate和LocalTime的信息,但是不包含ISO-8601日历系统中的时区信息。这里有一些关于LocalDate和LocalTime的例子: +如果你需要特定时区的data/time信息,则可以使用ZoneDateTime,它保存有ISO-8601日期系统的日期和时间,而且有时区信息。 + +**Nashorn JavaScript引擎** + +Java 8提供了新的Nashorn JavaScript引擎,使得我们可以在JVM上开发和运行JS应用。 +Nashorn JavaScript引擎是javax.script.ScriptEngine的另一个实现版本,这类Script引擎遵循相同的规则,允许Java和JavaScript交互使用,例子代码如下: + +**Base64** + +对 Base64 编码的支持已经被加入到Java 8官方库中,这样不需要使用第三方库就可以进行Base64编码,例子代码如下: + +```java +final String text = "Lets Learn Java 8!"; + +final String encoded = Base64 + .getEncoder() + .encodeToString(text.getBytes(StandardCharsets.UTF_8)); +System.out.println(encoded); + +final String decoded = new String( + Base64.getDecoder().decode(encoded), + StandardCharsets.UTF_8); +System.out.println(decoded); +``` + +新的Base64API也支持URL和MINE的编码解码。 diff --git a/java8-growing/pom.xml b/java8-growing/pom.xml new file mode 100644 index 0000000..13874e5 --- /dev/null +++ b/java8-growing/pom.xml @@ -0,0 +1,14 @@ + + + + learn-java8 + io.github.biezhi + 1.0-SNAPSHOT + + 4.0.0 + + java8-growing + + \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/AutoBoxing.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/AutoBoxing.java new file mode 100644 index 0000000..6fbf6f2 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/AutoBoxing.java @@ -0,0 +1,20 @@ +package io.github.biezhi.java8.growing.jdk5; + +/** + * 自动装箱、拆箱 + * + * @author biezhi + * @date 2018/2/8 + */ +public class AutoBoxing { + + public static void main(String[] args) { + int a = new Integer(66); + Integer b = 18; + + Boolean flag = true; + boolean isBug = Boolean.FALSE; + + } + +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Concurrent.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Concurrent.java new file mode 100644 index 0000000..9e83199 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Concurrent.java @@ -0,0 +1,85 @@ +package io.github.biezhi.java8.growing.jdk5; + +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.*; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 并发库 + * + * @author biezhi + * @date 2018/2/8 + */ +public class Concurrent { + + public void lock() { + Lock lock = new ReentrantLock(); + lock.lock(); + try { + System.out.println("hello world"); + } finally { + lock.unlock(); + } + } + + public void condition() throws InterruptedException { + Lock lock = new ReentrantLock(); + Condition condition = lock.newCondition(); + // do something + condition.await(10, TimeUnit.SECONDS); + System.out.println("Get result."); + } + + public void executorService() { + ExecutorService executorService = Executors.newFixedThreadPool(3); + executorService.submit(new Runnable() { + @Override + public void run() { + System.out.println("Task is running."); + } + }); + } + + public void blockingDeque() { + Queue blockingDeque = new ArrayBlockingQueue<>(20); + blockingDeque.add(1); + blockingDeque.add(2); + blockingDeque.add(3); + + blockingDeque.peek(); + } + + public void concurrentHashMap() { + Map concurrentHashMap = new ConcurrentHashMap<>(); + concurrentHashMap.put("Hello", 1); + concurrentHashMap.put("World", 2); + + System.out.println(concurrentHashMap.get("Hello")); + } + + public void copyOnWriteList() { + List copyOnWriteList = new CopyOnWriteArrayList<>(); + copyOnWriteList.add("a"); + copyOnWriteList.add("b"); + copyOnWriteList.add("c"); + + System.out.println(copyOnWriteList.size()); + } + + public void semaphore() { + Semaphore semaphore = new Semaphore(3); + try { + semaphore.acquire(); + System.out.println(Thread.currentThread().getName() + " is working"); + Thread.sleep(1000); + semaphore.release(); + System.out.println(Thread.currentThread().getName() + " is over"); + } catch (InterruptedException e) { + } + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/EnumDemo.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/EnumDemo.java new file mode 100644 index 0000000..e91d682 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/EnumDemo.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.growing.jdk5; + +/** + * 枚举 + * + * @author biezhi + * @date 2018/2/8 + */ +public enum EnumDemo { + + RED, GREEN, YELLOW + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/ForEach.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/ForEach.java new file mode 100644 index 0000000..3858a30 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/ForEach.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.growing.jdk5; + +import java.util.Arrays; +import java.util.List; + +/** + * for each + * + * @author biezhi + * @date 2018/2/8 + */ +public class ForEach { + + public static void main(String[] args) { + + int[] arr = {1, 4, 5, 7}; + + for (int i : arr) { + System.out.println(i); + } + + List names = Arrays.asList("王爵nice", "Gay冰", "A*熊"); + for (String name : names) { + System.out.println(name); + } + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Generic.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Generic.java new file mode 100644 index 0000000..591ccc0 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/Generic.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.growing.jdk5; + +import java.util.HashMap; +import java.util.Map; + +/** + * 泛型 + * + * @author biezhi + * @date 2018/2/8 + */ +public class Generic { + + public T getById(Integer id){ + return null; + } + + public static void main(String[] args) { + + Map map = new HashMap<>(); + + Generic generic = new Generic<>(); + + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/StaticImport.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/StaticImport.java new file mode 100644 index 0000000..98243f6 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/StaticImport.java @@ -0,0 +1,17 @@ +package io.github.biezhi.java8.growing.jdk5; + +import static java.lang.System.out; + +/** + * 静态导入 + * + * @author biezhi + * @date 2018/2/8 + */ +public class StaticImport { + + public static void main(String[] args) { + out.println("Hi let learn java 8."); + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/VarArgs.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/VarArgs.java new file mode 100644 index 0000000..6571aca --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk5/VarArgs.java @@ -0,0 +1,23 @@ +package io.github.biezhi.java8.growing.jdk5; + +import java.util.Arrays; +import java.util.List; + +/** + * 变长参数 + * + * @author biezhi + * @date 2018/2/8 + */ +public class VarArgs { + + public static List asList(String[] names){ + return Arrays.asList(names); + } + + public static void main(String[] args) { + List hello = Arrays.asList("王爵nice", "Gay冰", "A*熊"); + + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/CompilerAPI.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/CompilerAPI.java new file mode 100644 index 0000000..d7df659 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/CompilerAPI.java @@ -0,0 +1,131 @@ +package io.github.biezhi.java8.growing.jdk6; + +import javax.tools.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * 使用Compiler API + * + * @author biezhi + * @date 2018/2/8 + */ +public class CompilerAPI { + + public static void main(String[] args) throws Exception { + String program = "" + + "public class LearnJava6 {\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"欢迎你学习跟上 Java 8 之 CompilerAPI!\");\n" + + " }\n" + + "}\n"; + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + + JavaFileObject compilationUnit = + new StringJavaFileObject("LearnJava6", program); + + SimpleJavaFileManager fileManager = + new SimpleJavaFileManager(compiler.getStandardFileManager(null, null, null)); + + JavaCompiler.CompilationTask compilationTask = compiler.getTask( + null, fileManager, null, null, null, Arrays.asList(compilationUnit)); + + compilationTask.call(); + + CompiledClassLoader classLoader = + new CompiledClassLoader(fileManager.getGeneratedOutputFiles()); + + Class codeGenTest = classLoader.loadClass("LearnJava6"); + Method main = codeGenTest.getMethod("main", String[].class); + main.invoke(null, new Object[]{null}); + } + + private static class StringJavaFileObject extends SimpleJavaFileObject { + private final String code; + + public StringJavaFileObject(String name, String code) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), + Kind.SOURCE); + this.code = code; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return code; + } + } + + private static class ClassJavaFileObject extends SimpleJavaFileObject { + private final ByteArrayOutputStream outputStream; + private final String className; + + protected ClassJavaFileObject(String className, Kind kind) { + super(URI.create("mem:///" + className.replace('.', '/') + kind.extension), kind); + this.className = className; + outputStream = new ByteArrayOutputStream(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return outputStream; + } + + public byte[] getBytes() { + return outputStream.toByteArray(); + } + + public String getClassName() { + return className; + } + } + + private static class SimpleJavaFileManager extends ForwardingJavaFileManager { + private final List outputFiles; + + protected SimpleJavaFileManager(JavaFileManager fileManager) { + super(fileManager); + outputFiles = new ArrayList(); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + ClassJavaFileObject file = new ClassJavaFileObject(className, kind); + outputFiles.add(file); + return file; + } + + public List getGeneratedOutputFiles() { + return outputFiles; + } + } + + private static class CompiledClassLoader extends ClassLoader { + private final List files; + + private CompiledClassLoader(List files) { + this.files = files; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + Iterator itr = files.iterator(); + while (itr.hasNext()) { + ClassJavaFileObject file = itr.next(); + if (file.getClassName().equals(name)) { + itr.remove(); + byte[] bytes = file.getBytes(); + return super.defineClass(name, bytes, 0, bytes.length); + } + } + return super.findClass(name); + } + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/Console.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/Console.java new file mode 100644 index 0000000..8fd2202 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/Console.java @@ -0,0 +1,25 @@ +package io.github.biezhi.java8.growing.jdk6; + +/** + * 用Console开发控制台程序 + * + * @author biezhi + * @date 2018/2/8 + */ +public class Console { + + public static void main(String[] args) { + + java.io.Console console = System.console(); + + if (console != null) { + String user = new String(console.readLine(" Enter User: ", new Object[0])); + String pwd = new String(console.readPassword(" Enter Password: ", new Object[0])); + console.printf(" User name is:%s ", new Object[]{user}); + console.printf(" Password is:%s ", new Object[]{pwd}); + } else { + System.out.println(" No Console! "); + } + + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/DesktopTray.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/DesktopTray.java new file mode 100644 index 0000000..464f5ef --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/DesktopTray.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.growing.jdk6; + +/** + * Desktop类和SystemTray类 + * + * https://www.programcreek.com/java-api-examples/java.awt.Desktop + * + * @author biezhi + * @date 2018/2/8 + */ +public class DesktopTray { + +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/HttpServerAPI.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/HttpServerAPI.java new file mode 100644 index 0000000..ed3cdfb --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/HttpServerAPI.java @@ -0,0 +1,51 @@ +package io.github.biezhi.java8.growing.jdk6; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * Http Server + * + * @author biezhi + * @date 2018/2/8 + */ +public class HttpServerAPI { + + private static int count = 0; + + static class MyHandler implements HttpHandler { + @Override + public void handle(HttpExchange he) throws IOException { + System.out.println("Request " + count++); + System.out.println(he.getHttpContext().getPath()); + + InputStream is = he.getRequestBody(); + String response = "Lets Learn Java8."; + he.sendResponseHeaders(200, response.length()); + OutputStream os = he.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + } + + public static void main(String[] args) { + try { + HttpServer hs = HttpServer.create(new InetSocketAddress(8080), 0); + hs.createContext("/", new MyHandler()); + hs.createContext("/java", new MyHandler()); + hs.setExecutor(null); + hs.start(); + System.out.println("---begin---"); + System.out.println("Listening on " + hs.getAddress()); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/ScriptEngineDemo.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/ScriptEngineDemo.java new file mode 100644 index 0000000..9417ee6 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk6/ScriptEngineDemo.java @@ -0,0 +1,34 @@ +package io.github.biezhi.java8.growing.jdk6; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.io.FileReader; + +/** + * 对脚本语言的支持 + * + * @author biezhi + * @date 2018/2/8 + */ +public class ScriptEngineDemo { + + public static void main(String[] args) { + + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("ECMAScript"); + try { + String jsPath = ScriptEngineDemo.class.getResource("/test.js").getPath(); + + engine.eval(new FileReader(jsPath)); + + Invocable invokableEngine = (Invocable) engine; + + Object ret = invokableEngine.invokeFunction("test", null); + + System.out.println("The result is : " + ret); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/CatchMultiException.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/CatchMultiException.java new file mode 100644 index 0000000..34dfb7d --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/CatchMultiException.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.growing.jdk7; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * 捕获多异常 + * + * @author biezhi + * @date 2018/2/8 + */ +public class CatchMultiException { + + public static void main(String[] args) { + try { + BufferedReader reader = new BufferedReader(new FileReader("")); + Connection con = null; + Statement stmt = con.createStatement(); + } catch (IOException | SQLException e) { + //捕获多个异常,e就是final类型的 + e.printStackTrace(); + } + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/NumericUnderline.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/NumericUnderline.java new file mode 100644 index 0000000..2a2ea59 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/NumericUnderline.java @@ -0,0 +1,18 @@ +package io.github.biezhi.java8.growing.jdk7; + +/** + * 数字下划线支持 + * + * @author biezhi + * @date 2018/2/8 + */ +public class NumericUnderline { + + public static void main(String[] args) { + int num = 1234_5678_9; + float num2 = 222_33F; + long num3 = 123_000_111L; + long tenSenconds = 10_000L; + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/SwitchString.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/SwitchString.java new file mode 100644 index 0000000..d9d706e --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/SwitchString.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.growing.jdk7; + +import io.github.biezhi.java8.growing.jdk5.EnumDemo; + +/** + * switch对String的支持 + * + * @author biezhi + * @date 2018/2/8 + */ +public class SwitchString { + + public static void main(String[] args) { + String bis = "java"; + switch (bis) { + case "java": + break; + case "python": + break; + case "ruby": + break; + default: + break; + } + + EnumDemo enumDemo = EnumDemo.GREEN; + + switch (enumDemo) { + case RED: + break; + case YELLOW: + break; + default: + break; + } + + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TryWithResource.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TryWithResource.java new file mode 100644 index 0000000..189956f --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TryWithResource.java @@ -0,0 +1,30 @@ +package io.github.biezhi.java8.growing.jdk7; + +import io.github.biezhi.java8.growing.jdk6.ScriptEngineDemo; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * try-with-resource + * + * @author biezhi + * @date 2018/2/8 + */ +public class TryWithResource { + + public static void main(String[] args) { + String path = ScriptEngineDemo.class.getResource("/test.js").getPath(); + + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + String str = br.readLine(); + while (null != str) { + System.out.println(str); + str = br.readLine(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TypeInference.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TypeInference.java new file mode 100644 index 0000000..846803f --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk7/TypeInference.java @@ -0,0 +1,21 @@ +package io.github.biezhi.java8.growing.jdk7; + +import java.util.ArrayList; +import java.util.List; + +/** + * 类型推断 + * + * @author biezhi + * @date 2018/2/8 + */ +public class TypeInference { + + public static void main(String[] args) { + List list = new ArrayList<>(); + list.add("A"); + + List list2 = new ArrayList<>(); + list.addAll(list2); + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Base64Demo.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Base64Demo.java new file mode 100644 index 0000000..429eeb9 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Base64Demo.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Base64 增强 + * + * @author biezhi + * @date 2018/2/8 + */ +public class Base64Demo { + + public static void main(String[] args) { + final String text = "Lets Learn Java 8!"; + + final String encoded = Base64 + .getEncoder() + .encodeToString(text.getBytes(StandardCharsets.UTF_8)); + System.out.println(encoded); + + final String decoded = new String( + Base64.getDecoder().decode(encoded), + StandardCharsets.UTF_8); + System.out.println(decoded); + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DateTimeAPI.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DateTimeAPI.java new file mode 100644 index 0000000..9fab489 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DateTimeAPI.java @@ -0,0 +1,60 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.time.*; + +/** + * 新的日期时间 API + * + * @author biezhi + * @date 2018/2/8 + */ +public class DateTimeAPI { + + public static void main(String[] args) { + // Get the system clock as UTC offset + final Clock clock = Clock.systemUTC(); + System.out.println(clock.instant()); + System.out.println(clock.millis()); + + // Get the local date and local time + final LocalDate date = LocalDate.now(); + final LocalDate dateFromClock = LocalDate.now(clock); + + System.out.println(date); + System.out.println(dateFromClock); + + // Get the local date and local time + final LocalTime time = LocalTime.now(); + final LocalTime timeFromClock = LocalTime.now(clock); + + System.out.println(time); + System.out.println(timeFromClock); + + // Get the local date/time + final LocalDateTime datetime = LocalDateTime.now(); + final LocalDateTime datetimeFromClock = LocalDateTime.now(clock); + + System.out.println(datetime); + System.out.println(datetimeFromClock); + + // Get the zoned date/time + final ZonedDateTime zonedDatetime = ZonedDateTime.now(); + final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now(clock); + final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")); + + System.out.println(zonedDatetime); + System.out.println(zonedDatetimeFromClock); + System.out.println(zonedDatetimeFromZone); + + // Get duration between two dates + final LocalDateTime from = LocalDateTime.of(2014, Month.APRIL, 16, 0, 0, 0); + final LocalDateTime to = LocalDateTime.of(2015, Month.APRIL, 16, 23, 59, 59); + + final Duration duration = Duration.between(from, to); + System.out.println("Duration in days: " + duration.toDays()); + System.out.println("Duration in hours: " + duration.toHours()); + + + } + +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DefaulableFactory.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DefaulableFactory.java new file mode 100644 index 0000000..6d9b085 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/DefaulableFactory.java @@ -0,0 +1,10 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.util.function.Supplier; + +public interface DefaulableFactory { + // Interfaces now allow static methods + static Integer create(Supplier supplier) { + return supplier.get(); + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Functional.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Functional.java new file mode 100644 index 0000000..f7b849f --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Functional.java @@ -0,0 +1,14 @@ +package io.github.biezhi.java8.growing.jdk8; + +/** + * 函数式接口 + * + * @author biezhi + * @date 2018/2/8 + */ +@FunctionalInterface +public interface Functional { + + void method(); + +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/FunctionalDefaultMethods.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/FunctionalDefaultMethods.java new file mode 100644 index 0000000..b760d89 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/FunctionalDefaultMethods.java @@ -0,0 +1,16 @@ +package io.github.biezhi.java8.growing.jdk8; + +/** + * 默认方法 + * + * @author biezhi + * @date 2018/2/8 + */ +@FunctionalInterface +public interface FunctionalDefaultMethods { + + void method(); + + default void defaultMethod() { + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Lambda.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Lambda.java new file mode 100644 index 0000000..185c471 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Lambda.java @@ -0,0 +1,17 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.util.Arrays; + +/** + * Lambda 表达式 + * + * @author biezhi + * @date 2018/2/8 + */ +public class Lambda { + + public static void main(String[] args) { + Arrays.asList("a", "b", "d").forEach(System.out::println); + + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/NashornDemo.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/NashornDemo.java new file mode 100644 index 0000000..11860e9 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/NashornDemo.java @@ -0,0 +1,22 @@ +package io.github.biezhi.java8.growing.jdk8; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +/** + * Nashorn JavaScript引擎 + * + * @author biezhi + * @date 2018/2/8 + */ +public class NashornDemo { + + public static void main(String[] args) throws ScriptException { + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("JavaScript"); + + System.out.println(engine.getClass().getName()); + System.out.println("Result:" + engine.eval("function f() { return 1; }; f() + 1;")); + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/OptionalDemo.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/OptionalDemo.java new file mode 100644 index 0000000..a5ff71c --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/OptionalDemo.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.util.Optional; + +/** + * Optional + * + * @author biezhi + * @date 2018/2/8 + */ +public class OptionalDemo { + + public static void main(String[] args) { + Optional fullName = Optional.ofNullable(null); + System.out.println("Full Name is set? " + fullName.isPresent()); + System.out.println("Full Name: " + fullName.orElse("[none]")); + System.out.println(fullName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!")); + + Optional firstName = Optional.of("Tom"); + System.out.println("First Name is set? " + firstName.isPresent()); + System.out.println("First Name: " + firstName.orElseGet(() -> "[none]")); + System.out.println(firstName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!")); + System.out.println(); + + } +} diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/ParallelArrays.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/ParallelArrays.java new file mode 100644 index 0000000..489b3e8 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/ParallelArrays.java @@ -0,0 +1,28 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 并行数组 + * + * @author biezhi + * @date 2018/2/8 + */ +public class ParallelArrays { + + public static void main(String[] args) { + long[] arrayOfLong = new long[20000]; + + Arrays.parallelSetAll(arrayOfLong, + index -> ThreadLocalRandom.current().nextInt(1000000)); + Arrays.stream(arrayOfLong).limit(10).forEach( + i -> System.out.print(i + " ")); + System.out.println(); + + Arrays.parallelSort(arrayOfLong); + Arrays.stream(arrayOfLong).limit(10).forEach( + i -> System.out.print(i + " ")); + System.out.println(); + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Streams.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Streams.java new file mode 100644 index 0000000..8acc7dd --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/Streams.java @@ -0,0 +1,51 @@ +package io.github.biezhi.java8.growing.jdk8; + +import java.util.Arrays; +import java.util.Collection; + +public class Streams { + + private enum Status { + OPEN, CLOSED + } + + private static final class Task { + private final Status status; + private final Integer points; + + Task(final Status status, final Integer points) { + this.status = status; + this.points = points; + } + + public Integer getPoints() { + return points; + } + + public Status getStatus() { + return status; + } + + @Override + public String toString() { + return String.format("[%s, %d]", status, points); + } + } + + public static void main(String[] args) { + final Collection tasks = Arrays.asList( + new Task(Status.OPEN, 5), + new Task(Status.OPEN, 13), + new Task(Status.CLOSED, 8) + ); + + // Calculate total points of all active tasks using sum() + final long totalPointsOfOpenTasks = tasks + .stream() + .filter(task -> task.getStatus() == Status.OPEN) + .mapToInt(Task::getPoints) + .sum(); + + System.out.println("Total points: " + totalPointsOfOpenTasks); + } +} \ No newline at end of file diff --git a/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/TypeInference.java b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/TypeInference.java new file mode 100644 index 0000000..fad3b92 --- /dev/null +++ b/java8-growing/src/main/java/io/github/biezhi/java8/growing/jdk8/TypeInference.java @@ -0,0 +1,24 @@ +package io.github.biezhi.java8.growing.jdk8; + +/** + * 更好的类型推断 + * + * @author biezhi + * @date 2018/2/8 + */ +public class TypeInference { + + public static T defaultValue() { + return null; + } + + public T getOrDefault(T value, T defaultValue) { + return (value != null) ? value : defaultValue; + } + + public static void main(String[] args) { + final TypeInference typeInference = new TypeInference<>(); + typeInference.getOrDefault("22", TypeInference.defaultValue()); + } + +} \ No newline at end of file diff --git a/java8-growing/src/main/resources/test.js b/java8-growing/src/main/resources/test.js new file mode 100644 index 0000000..c9f12cc --- /dev/null +++ b/java8-growing/src/main/resources/test.js @@ -0,0 +1,3 @@ +function test(){ + return Math.round( 11.2 ); +} \ No newline at end of file diff --git a/java8-lambda/README.md b/java8-lambda/README.md new file mode 100644 index 0000000..2e29591 --- /dev/null +++ b/java8-lambda/README.md @@ -0,0 +1,74 @@ +# lambda 表达式 + +## 命令式和函数式 + +**命令式编程**:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。 +**声明式编程**:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。 + +## 什么是函数式编程? + +每个人对函数式编程的理解不尽相同。 我的理解是:**在完成一个编程任务时,通过使用不可变的值或函数,对他们进行处理,然后得到另一个值的过程。** +不同的语言社区往往对各自语言中的特性孤芳自赏。现在谈 Java 程序员如何定义函数式编程还为时尚早,但是,这根本不重要! +我们关心的是如何写出好代码,而不是符合函数式编程风格的代码。 + +## 行为参数化 + +把算法的策略(行为)作为一个参数传递给函数。 + +## lambda 管中窥豹 + +* 匿名:它不像普通的方法那样有一个明确的名称:写得少而想得多! +* 函数:Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。 +* 传递:Lambda表达式可以作为参数传递给方法或存储在变量中。 +* 简洁:无需像匿名类那样写很多模板代码。 + +## 函数描述符 + +函数式接口的抽象方法的签名基本上就是Lambda表达式的签名,这种抽象方法叫作函数描述符。 + +## 函数式接口,类型推断 + +函数式接口定义且只定义了一个抽象方法,因为抽象方法的签名可以描述Lambda表达式的签名。 +函数式接口的抽象方法的签名称为函数描述符。 +所以为了应用不同的Lambda表达式,你需要一套能够描述常见函数描述符的函数式接口。 + +## Java 8中的常用函数式接口 + +| 函数式接口 | 函数描述符 | 原始类型特化 | +|:-----:|:--------|:-------| +| `Predicate` | `T->boolean` | `IntPredicate,LongPredicate, DoublePredicate` | +| `Consumer` | `T->void` | `IntConsumer,LongConsumer, DoubleConsumer` | +| `Function` | `T->R` | `IntFunction, IntToDoubleFunction,`
`IntToLongFunction, LongFunction,`
`LongToDoubleFunction, LongToIntFunction, `
`DoubleFunction, ToIntFunction, `
`ToDoubleFunction, ToLongFunction` | +| `Supplier` | `()->T` | `BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier` | +| `UnaryOperator` | `T->T` | `IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator` | +| `BinaryOperator` | `(T,T)->T` | `IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator` | +| `BiPredicate` | `(L,R)->boolean` | | +| `BiConsumer` | `(T,U)->void` | `ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumer` | +| `BiFunction` | `(T,U)->R` | `ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction` | + + +## Lambdas及函数式接口的例子 + +| 使用案例 | Lambda 的例子 | 对应的函数式接口 | +|:-----:|:--------|:-------| +| 布尔表达式 | `(List list) -> list.isEmpty()` | `Predicate>` | +| 创建对象 | `() -> new Project()` | `Supplier` | +| 消费一个对象 | `(Project p) -> System.out.println(p.getStars())` | `Consumer` | +| 从一个对象中选择/提取 | `(int a, int b) -> a * b` | `IntBinaryOperator` | +| 比较两个对象 | `(Project p1, Project p2) -> p1.getStars().compareTo(p2.getStars())` | `Comparator 或 BiFunction `Project, Integer> 或 ToIntBiFunction` | + +## 方法引用 + +方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。 + +## 本节课小结 + +- lambda 表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回 类型,可能还有一个可以抛出的异常的列表。 +- lambda 表达式让你可以简洁地传递代码。 +- 函数式接口就是仅仅声明了一个抽象方法的接口。 +- 只有在接受函数式接口的地方才可以使用 lambda 表达式。 +- lambda 表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。 +- Java 8自带一些常用的函数式接口,放在 `java.util.function` 包里,包括 `Predicate`、`Function`、`Supplier`、`Consumer` 和 `BinaryOperator`。 +- Lambda表达式所需要代表的类型称为目标类型。 +- 方法引用让你重复使用现有的方法实现并直接传递它们。 +- `Comparator``、`Predicate` 和 `Function` 等函数式接口都有几个可以用来结合 lambda 表达式的默认方法。 \ No newline at end of file diff --git a/java8-lambda/pom.xml b/java8-lambda/pom.xml index 2f46a4e..684c170 100644 --- a/java8-lambda/pom.xml +++ b/java8-lambda/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/FilterProjects.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/FilterProjects.java new file mode 100644 index 0000000..3c803b6 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/FilterProjects.java @@ -0,0 +1,147 @@ +package io.github.biezhi.java8.lambda.lesson1; + +import io.github.biezhi.java8.lambda.lesson1.predicate.ProjectLanguagePredicate; +import io.github.biezhi.java8.lambda.lesson1.predicate.ProjectStarPredicate; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +/** + * 过滤 Project + * + * @author biezhi + * @date 2018/2/9 + */ +public class FilterProjects { + + /** + * 过滤 Java 项目 + * + * @param projects + * @return + */ + public static List filterJavaProjects(List projects) { + List result = new ArrayList<>(); + for (Project project : projects) { + if ("java".equals(project.getLanguage())) { + result.add(project); + } + } + return result; + } + + /** + * 按语言过滤 + * + * @param projects + * @param language + * @return + */ + public static List filterLanguageProjects(List projects, String language) { + List result = new ArrayList<>(); + for (Project project : projects) { + if (language.equals(project.getLanguage())) { + result.add(project); + } + } + return result; + } + + /** + * 按语言和 star 数过滤 + * + * @param projects + * @param language + * @param stars + * @return + */ + public static List filterLanguageAndStarProjects(List projects, String language, int stars) { + List result = new ArrayList<>(); + for (Project project : projects) { + if (language.equals(project.getLanguage()) && project.getStars() > stars) { + result.add(project); + } + } + return result; + } + + /** + * 按照断言条件过滤 + * + * @param projects + * @param projectPredicate + * @return + */ + public static List filterProjects(List projects, ProjectPredicate projectPredicate) { + List result = new ArrayList<>(); + for (Project project : projects) { + if (projectPredicate.test(project)) { + result.add(project); + } + } + return result; + } + + public static List filter(List list, Predicate predicate){ + List result = new ArrayList<>(); + for (T t : list) { + if(predicate.test(t)){ + result.add(t); + } + } + return result; + } + + public static void main(String[] args) { + List data = new ArrayList<>(); + + data.add(Project.builder().name("Blade").language("java").author("biezhi") + .stars(3500).description("Lightning fast and elegant mvc framework for Java8").build()); + + data.add(Project.builder().name("Tale").language("java").author("biezhi") + .stars(2600).description("Best beautiful java blog, worth a try").build()); + + data.add(Project.builder().name("Vue.js").language("js").author("yyx990803") + .stars(83000).description("A progressive, incrementally-adoptable JavaScript framework for building UI on the web.").build()); + + data.add(Project.builder().name("Flask").language("python").author("pallets") + .stars(10500).description("The Python micro framework for building web applications").build()); + + data.add(Project.builder().name("Elves").language("java").author("biezhi") + .stars(200).description("Spider").build()); + + List projects = filterJavaProjects(data); + + projects = filterLanguageProjects(data, "python"); + + projects = filterLanguageAndStarProjects(data, "js", 1000); + + projects = filterProjects(data, new ProjectLanguagePredicate("python")); + + projects = filterProjects(data, new ProjectStarPredicate(1000)); + + System.out.println(projects.size()); + +// JButton jButton = null; +// jButton.addActionListener(e -> System.out.println("按钮被按下了")); + + // 1. 值参数化:啰嗦、死板 + // 2. 行为参数化:简洁、灵活 + + + List filter = filter(data, project -> project.getStars() > 1000); + + data.sort(Comparator.comparing(Project::getStars)); + + System.out.println(data); + + Runnable task = () -> System.out.println("Hello World"); + + Thread t = new Thread(task); + t.start(); + + } + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/Project.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/Project.java new file mode 100644 index 0000000..6992539 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/Project.java @@ -0,0 +1,41 @@ +package io.github.biezhi.java8.lambda.lesson1; + +import lombok.Builder; +import lombok.Data; + +/** + * 项目 + * + * @author biezhi + * @date 2018/2/9 + */ +@Data +@Builder +public class Project { + + /** + * 项目名称 + */ + private String name; + + /** + * 编程语言 + */ + private String language; + + /** + * star 数 + */ + private Integer stars; + + /** + * 描述 + */ + private String description; + + /** + * 作者 + */ + private String author; + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/ProjectPredicate.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/ProjectPredicate.java new file mode 100644 index 0000000..ac0538b --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/ProjectPredicate.java @@ -0,0 +1,13 @@ +package io.github.biezhi.java8.lambda.lesson1; + +/** + * 项目过滤接口 + * + * @author biezhi + * @date 2018/2/9 + */ +public interface ProjectPredicate { + + boolean test(Project project); + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectLanguagePredicate.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectLanguagePredicate.java new file mode 100644 index 0000000..af2b13c --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectLanguagePredicate.java @@ -0,0 +1,24 @@ +package io.github.biezhi.java8.lambda.lesson1.predicate; + +import io.github.biezhi.java8.lambda.lesson1.Project; +import io.github.biezhi.java8.lambda.lesson1.ProjectPredicate; + +/** + * 按编程语言过滤 + * + * @author biezhi + * @date 2018/2/9 + */ +public class ProjectLanguagePredicate implements ProjectPredicate { + + private String language; + + public ProjectLanguagePredicate(String language) { + this.language = language; + } + + @Override + public boolean test(Project project) { + return language.equals(project.getLanguage()); + } +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectStarPredicate.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectStarPredicate.java new file mode 100644 index 0000000..3be9429 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson1/predicate/ProjectStarPredicate.java @@ -0,0 +1,25 @@ +package io.github.biezhi.java8.lambda.lesson1.predicate; + +import io.github.biezhi.java8.lambda.lesson1.Project; +import io.github.biezhi.java8.lambda.lesson1.ProjectPredicate; + +/** + * 按 star 数过滤 + * + * @author biezhi + * @date 2018/2/9 + */ +public class ProjectStarPredicate implements ProjectPredicate { + + private Integer stars; + + public ProjectStarPredicate(Integer stars) { + this.stars = stars; + } + + @Override + public boolean test(Project project) { + return project.getStars() > stars; + } + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson2/FunctionalDemo.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson2/FunctionalDemo.java new file mode 100644 index 0000000..23e4498 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson2/FunctionalDemo.java @@ -0,0 +1,81 @@ +package io.github.biezhi.java8.lambda.lesson2; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.*; + +/** + * 函数式接口示例 + * + * @author biezhi + * @date 2018/2/10 + */ +public class FunctionalDemo { + + /** + * 断言 + */ + public void predicate() { + Predicate namesStartingWithS = name -> name.startsWith("s"); + boolean hello = namesStartingWithS.test("Hello"); + // false + } + + /** + * 消费数据 + */ + public void consumer() { + Consumer messageConsumer = message -> System.out.println(message); + messageConsumer.accept("Learn Java8"); // Learn Java8" + + } + + /** + * 转换 + */ + public void function() { + Function toUpperCase = name -> name.toUpperCase(); + toUpperCase.apply("Java"); // Java + } + + /** + * 提供数据 + */ + public void supplier() { + Supplier uuidGenerator = () -> UUID.randomUUID().toString(); + System.out.println(uuidGenerator.get()); + + } + + public static void main(String[] args) { + + List list = new ArrayList<>(); + for (int i = 300; i < 400; i++) { + list.add(i); + } + + IntPredicate evenNumbers = (int i) -> i % 2 == 0; + evenNumbers.test(1000); + + Predicate oddNumbers = (Integer i) -> i % 2 == 1; + oddNumbers.test(1000); + + Function add1 = x -> x + 1; + Function concat = x -> x + 1; + + Integer two = add1.apply(1); //yields 2 + String answer = concat.apply("0 + 1 = "); // "0 + 1 = 1" + + BinaryOperator sum = (a, b) -> a + b; + Integer res = sum.apply(1, 2); // 3 + + BinaryOperator> compose = (f, g) -> x -> g.apply(f.apply(x)); + + UnaryOperator add2 = n -> n + 1; + UnaryOperator concat1 = s -> s + 1; + Function> sum2 = x -> y -> x + y; + UnaryOperator sum10 = sum2.apply(10); + + } +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/ConstructorReference.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/ConstructorReference.java new file mode 100644 index 0000000..6fb0011 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/ConstructorReference.java @@ -0,0 +1,23 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import java.util.function.Supplier; + +/** + * 构造器引用 + *

+ * 对于一个现有构造函数, 你可以利用它的名称和关键字 new 来创建它的一个引用: ClassName::new。 + * 它的功能与指向静态方法的引用类似。 + * + * @author biezhi + * @date 2018/2/10 + */ +public class ConstructorReference { + + public static void main(String[] args) { + //构造器引用 + //根据参数列表自动匹配构造器 + Supplier sup = ConstructorReference::new; + ConstructorReference constructorReference = sup.get(); + } + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/DoneByYou.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/DoneByYou.java new file mode 100644 index 0000000..121891c --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/DoneByYou.java @@ -0,0 +1,22 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import java.util.List; +import java.util.function.BiPredicate; +import java.util.function.Function; + +/** + * 由你完成的 + *

+ * 下列Lambda表达式的等效方法引用是什么 + * + * @author biezhi + * @date 2018/2/10 + */ +public class DoneByYou { + + public static void main(String[] args) { + Function stringToInteger = (String s) -> Integer.parseInt(s); + BiPredicate, String> contains = (list, element) -> list.contains(element); + + } +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/LambdaException.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/LambdaException.java new file mode 100644 index 0000000..97e3700 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/LambdaException.java @@ -0,0 +1,29 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.function.Function; + +/** + * lambda 中有异常 + *

+ * 任何函数式接口都不允许抛出受检异常 + *

+ * sf上这个问题的一些讨论:https://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception + * + * @author biezhi + * @date 2018/2/10 + */ +public class LambdaException { + + public static void main(String[] args) { + Function f = (BufferedReader b) -> { + try { + return b.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + } + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/Lambdas.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/Lambdas.java new file mode 100644 index 0000000..a162e7b --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/Lambdas.java @@ -0,0 +1,85 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import io.github.biezhi.java8.lambda.lesson1.Project; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * lambdas + * + * @author biezhi + * @date 2018/2/10 + */ +public class Lambdas { + + public static List buildData() { + List data = new ArrayList<>(); + + data.add(Project.builder().name("Blade").language("java").author("biezhi") + .stars(3500).description("Lightning fast and elegant mvc framework for Java8").build()); + + data.add(Project.builder().name("Tale").language("java").author("biezhi") + .stars(2600).description("Best beautiful java blog, worth a try").build()); + + data.add(Project.builder().name("Vue.js").language("js").author("yyx990803") + .stars(83000).description("A progressive, incrementally-adoptable JavaScript framework for building UI on the web.").build()); + + data.add(Project.builder().name("Flask").language("python").author("pallets") + .stars(10500).description("The Python micro framework for building web applications").build()); + + data.add(Project.builder().name("Elves").language("java").author("biezhi") + .stars(200).description("Spider").build()); + + return data; + } + + public static void main(String[] args) { + List projects = buildData(); +// List names = getNames(projects); +// List names = getNames(projects, project -> project.getStars() > 1000); + List names = getNames(projects, project -> project.getStars() > 1000, project -> project.getDescription()); + List stars = getNames(projects, project -> project.getStars() > 1000, ProjectFunction.buildStarFunction()); + System.out.println(stars); +// names.forEach(name -> System.out.println(name)); + } + + public static List getNames(List projects) { + List names = new ArrayList<>(); + for (Project project : projects) { + names.add(project.getName()); + } + return names; + } + + public static List getNames(List projects, Predicate predicate) { + List names = new ArrayList<>(); + for (Project project : projects) { + if (predicate.test(project)) { + names.add(project.getName()); + } + } + return names; + } + + public static List getNames(List projects, Predicate predicate, Function function) { + List names = new ArrayList<>(); + for (Project project : projects) { + if (predicate.test(project)) { + names.add(function.apply(project)); + } + } + return names; + } + +} + +interface ProjectFunction extends Function { + + static ProjectFunction buildStarFunction() { + return Project::getStars; + } + +} \ No newline at end of file diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/MethodReference.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/MethodReference.java new file mode 100644 index 0000000..f4e55f2 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/MethodReference.java @@ -0,0 +1,48 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import io.github.biezhi.java8.lambda.lesson1.Project; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toList; + +/** + * 方法引用 + *

+ * 1. 指向静态方法的方法引用 + * 2. 指向现有对象的实例方法的方法引用 + * + * @author biezhi + * @date 2018/2/10 + */ +public class MethodReference { + + public static List findNumbers(List numbers, Predicate filter) { + List numbersFound = numbers + .stream() + .filter(filter) + .collect(toList()); + + return numbersFound; + } + + public static boolean multipleOf3(Integer number) { + return (number % 3) == 0; + } + + public static void main(String[] args) { + List numbers = Arrays.asList(1, 3, 6, 8, 9, 12, 14, 15); + + List multiplesOf3 = findNumbers(numbers, MethodReference::multipleOf3); + System.out.println(multiplesOf3.contains(3)); + + Project project = Project.builder().name("Blade").build(); + Arrays.asList(project).stream() + .map(Project::getName) + .count(); + + } + +} diff --git a/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/OtherReference.java b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/OtherReference.java new file mode 100644 index 0000000..6ac2f59 --- /dev/null +++ b/java8-lambda/src/main/java/io/github/biezhi/java8/lambda/lesson3/OtherReference.java @@ -0,0 +1,23 @@ +package io.github.biezhi.java8.lambda.lesson3; + +import java.util.function.Function; + +/** + * 数组引用 + * + * @author biezhi + * @date 2018/2/10 + */ +public class OtherReference { + + public static void main(String[] args) { + Function fun = x -> new String[x]; + String[] strs = fun.apply(10); + System.out.println(strs.length); + + Function fun1 = String[]::new; + strs = fun1.apply(20); + System.out.println(strs.length); + } + +} diff --git a/java8-nashorn/pom.xml b/java8-nashorn/pom.xml index 4bc08d2..b0b5bf4 100644 --- a/java8-nashorn/pom.xml +++ b/java8-nashorn/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn1.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn1.java new file mode 100644 index 0000000..170dd94 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn1.java @@ -0,0 +1,31 @@ +package io.github.biezhi.java8.nashorn; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.io.FileReader; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * Calling javascript functions from java with nashorn. + * + * @author Benjamin Winterberg + */ +public class Nashorn1 { + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval(new FileReader("java8-nashorn/src/main/resources/nashorn1.js")); + + Invocable invocable = (Invocable) engine; + Object result = invocable.invokeFunction("fun1", "Peter Parker"); + System.out.println(result); + System.out.println(result.getClass()); + + invocable.invokeFunction("fun2", new Date()); + invocable.invokeFunction("fun2", LocalDateTime.now()); + invocable.invokeFunction("fun2", new Person()); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn10.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn10.java new file mode 100644 index 0000000..71eafe2 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn10.java @@ -0,0 +1,27 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.NashornScriptEngine; + +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.util.concurrent.TimeUnit; + +/** + * @author Benjamin Winterberg + */ +public class Nashorn10 { + + public static void main(String[] args) throws ScriptException, NoSuchMethodException { + NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn10.js')"); + + long t0 = System.nanoTime(); + + for (int i = 0; i < 100000; i++) { + engine.invokeFunction("testPerf"); + } + + long took = System.nanoTime() - t0; + System.out.format("Elapsed time: %d ms", TimeUnit.NANOSECONDS.toMillis(took)); + } +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn11.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn11.java new file mode 100644 index 0000000..2ef3d56 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn11.java @@ -0,0 +1,157 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.NashornScriptEngine; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.script.SimpleBindings; +import javax.script.SimpleScriptContext; + +/** + * @author Benjamin Winterberg + */ +public class Nashorn11 { + + public static void main(String[] args) throws Exception { +// test1(); +// test2(); +// test3(); +// test4(); +// test5(); +// test6(); +// test7(); + test8(); + } + + private static void test8() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + engine.eval("var obj = { foo: 23 };"); + + ScriptContext defaultContext = engine.getContext(); + Bindings defaultBindings = defaultContext.getBindings(ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context1 = new SimpleScriptContext(); + context1.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context2 = new SimpleScriptContext(); + context2.getBindings(ScriptContext.ENGINE_SCOPE).put("obj", defaultBindings.get("obj")); + + engine.eval("obj.foo = 44;", context1); + engine.eval("print(obj.foo);", context1); + engine.eval("print(obj.foo);", context2); + } + + private static void test7() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + engine.eval("var foo = 23;"); + + ScriptContext defaultContext = engine.getContext(); + Bindings defaultBindings = defaultContext.getBindings(ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context1 = new SimpleScriptContext(); + context1.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context2 = new SimpleScriptContext(); + context2.getBindings(ScriptContext.ENGINE_SCOPE).put("foo", defaultBindings.get("foo")); + + engine.eval("foo = 44;", context1); + engine.eval("print(foo);", context1); + engine.eval("print(foo);", context2); + } + + private static void test6() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + ScriptContext defaultContext = engine.getContext(); + defaultContext.getBindings(ScriptContext.GLOBAL_SCOPE).put("foo", "hello"); + + ScriptContext customContext = new SimpleScriptContext(); + customContext.setBindings(defaultContext.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.ENGINE_SCOPE); + + Bindings bindings = new SimpleBindings(); + bindings.put("foo", "world"); + customContext.setBindings(bindings, ScriptContext.GLOBAL_SCOPE); + +// engine.eval("foo = 23;"); // overrides foo in all contexts, why??? + + engine.eval("print(foo)"); // hello + engine.eval("print(foo)", customContext); // world + engine.eval("print(foo)", defaultContext); // hello + } + + private static void test5() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + engine.eval("var obj = { foo: 'foo' };"); + engine.eval("function printFoo() { print(obj.foo) };"); + + ScriptContext defaultContext = engine.getContext(); + Bindings defaultBindings = defaultContext.getBindings(ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context1 = new SimpleScriptContext(); + context1.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context2 = new SimpleScriptContext(); + context2.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + engine.eval("obj.foo = 'bar';", context1); + engine.eval("printFoo();", context1); + engine.eval("printFoo();", context2); + } + + private static void test4() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + engine.eval("function foo() { print('bar') };"); + + ScriptContext defaultContext = engine.getContext(); + Bindings defaultBindings = defaultContext.getBindings(ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context = new SimpleScriptContext(); + context.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + engine.eval("foo();", context); + System.out.println(context.getAttribute("foo")); + } + + private static void test3() throws ScriptException { + NashornScriptEngine engine = createEngine(); + + ScriptContext defaultContext = engine.getContext(); + Bindings defaultBindings = defaultContext.getBindings(ScriptContext.ENGINE_SCOPE); + + SimpleScriptContext context = new SimpleScriptContext(); + context.setBindings(defaultBindings, ScriptContext.ENGINE_SCOPE); + + engine.eval("function foo() { print('bar') };", context); + engine.eval("foo();", context); + + Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE); + System.out.println(bindings.get("foo")); + System.out.println(context.getAttribute("foo")); + } + + private static void test2() throws ScriptException { + NashornScriptEngine engine = createEngine(); + engine.eval("function foo() { print('bar') };"); + + SimpleScriptContext context = new SimpleScriptContext(); + engine.eval("print(Function);", context); + engine.eval("foo();", context); + } + + private static void test1() throws ScriptException { + NashornScriptEngine engine = createEngine(); + engine.eval("function foo() { print('bar') };"); + engine.eval("foo();"); + } + + private static NashornScriptEngine createEngine() { + return (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn2.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn2.java new file mode 100644 index 0000000..b89bda9 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn2.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.ScriptObjectMirror; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.io.FileReader; +import java.util.Arrays; + +/** + * Calling java methods from javascript with nashorn. + * + * @author Benjamin Winterberg + */ +public class Nashorn2 { + + public static String fun(String name) { + System.out.format("Hi there from Java, %s", name); + return "greetings from java"; + } + + public static void fun2(Object object) { + System.out.println(object.getClass()); + } + + public static void fun3(ScriptObjectMirror mirror) { + System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true))); + } + + public static void fun4(ScriptObjectMirror person) { + System.out.println("Full Name is: " + person.callMember("getFullName")); + } + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval(new FileReader("java8-nashorn/src/main/resources/nashorn2.js")); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn3.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn3.java new file mode 100644 index 0000000..fba357a --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn3.java @@ -0,0 +1,18 @@ +package io.github.biezhi.java8.nashorn; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +/** + * Working with java types from javascript. + * + * @author Benjamin Winterberg + */ +public class Nashorn3 { + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn3.js')"); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn4.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn4.java new file mode 100644 index 0000000..1a1642e --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn4.java @@ -0,0 +1,18 @@ +package io.github.biezhi.java8.nashorn; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +/** + * Working with java types from javascript. + * + * @author Benjamin Winterberg + */ +public class Nashorn4 { + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("loadWithNewGlobal('java8-nashorn/src/main/resources/nashorn4.js')"); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn5.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn5.java new file mode 100644 index 0000000..47c0d94 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn5.java @@ -0,0 +1,29 @@ +package io.github.biezhi.java8.nashorn; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +/** + * Bind java objects to custom javascript objects. + * + * @author Benjamin Winterberg + */ +public class Nashorn5 { + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn5.js')"); + + Invocable invocable = (Invocable) engine; + + Product product = new Product(); + product.setName("Rubber"); + product.setPrice(1.99); + product.setStock(1037); + + Object result = invocable.invokeFunction("getValueOfGoods", product); + System.out.println(result); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn6.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn6.java new file mode 100644 index 0000000..2e1adb7 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn6.java @@ -0,0 +1,36 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.ScriptObjectMirror; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +/** + * Using Backbone Models from Nashorn. + * + * @author Benjamin Winterberg + */ +public class Nashorn6 { + + public static void main(String[] args) throws Exception { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn6.js')"); + + Invocable invocable = (Invocable) engine; + + Product product = new Product(); + product.setName("Rubber"); + product.setPrice(1.99); + product.setStock(1337); + + ScriptObjectMirror result = (ScriptObjectMirror) + invocable.invokeFunction("calculate", product); + System.out.println(result.get("name") + ": " + result.get("valueOfGoods")); + } + + public static void getProduct(ScriptObjectMirror result) { + System.out.println(result.get("name") + ": " + result.get("valueOfGoods")); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn7.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn7.java new file mode 100644 index 0000000..8f7b584 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn7.java @@ -0,0 +1,43 @@ +package io.github.biezhi.java8.nashorn; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +/** + * @author Benjamin Winterberg + */ +public class Nashorn7 { + + public static class Person { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getLengthOfName() { + return name.length(); + } + } + + public static void main(String[] args) throws ScriptException, NoSuchMethodException { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("function foo(predicate, obj) { return !!(eval(predicate)); };"); + + Invocable invocable = (Invocable) engine; + + Person person = new Person(); + person.setName("Hans"); + + String predicate = "obj.getLengthOfName() >= 4"; + Object result = invocable.invokeFunction("foo", predicate, person); + System.out.println(result); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn8.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn8.java new file mode 100644 index 0000000..c06893e --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn8.java @@ -0,0 +1,22 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.NashornScriptEngine; + +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +/** + * @author Benjamin Winterberg + */ +public class Nashorn8 { + public static void main(String[] args) throws ScriptException, NoSuchMethodException { + NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn8.js')"); + + engine.invokeFunction("evaluate1"); // [object global] + engine.invokeFunction("evaluate2"); // [object Object] + engine.invokeFunction("evaluate3", "Foobar"); // Foobar + engine.invokeFunction("evaluate3", new Person("John", "Doe")); // [object global] <- ??????? + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn9.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn9.java new file mode 100644 index 0000000..9766792 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Nashorn9.java @@ -0,0 +1,31 @@ +package io.github.biezhi.java8.nashorn; + +import jdk.nashorn.api.scripting.NashornScriptEngine; + +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.util.concurrent.TimeUnit; + +/** + * @author Benjamin Winterberg + */ +public class Nashorn9 { + + public static void main(String[] args) throws ScriptException, NoSuchMethodException { + NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); + engine.eval("load('java8-nashorn/src/main/resources/nashorn9.js')"); + + long t0 = System.nanoTime(); + + double result = 0; + for (int i = 0; i < 1000; i++) { + double num = (double) engine.invokeFunction("testPerf"); + result += num; + } + + System.out.println(result > 0); + + long took = System.nanoTime() - t0; + System.out.format("Elapsed time: %d ms", TimeUnit.NANOSECONDS.toMillis(took)); + } +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Person.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Person.java new file mode 100644 index 0000000..5715159 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Person.java @@ -0,0 +1,16 @@ +package io.github.biezhi.java8.nashorn; + +/** +* @author Benjamin Winterberg +*/ +public class Person { + public String firstName; + public String lastName; + + public Person() {} + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Product.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Product.java new file mode 100644 index 0000000..e97a6c8 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/Product.java @@ -0,0 +1,12 @@ +package io.github.biezhi.java8.nashorn; + +import lombok.Data; + +@Data +public class Product { + private String name; + private double price; + private int stock; + private double valueOfGoods; + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/SuperRunner.java b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/SuperRunner.java new file mode 100644 index 0000000..00120c5 --- /dev/null +++ b/java8-nashorn/src/main/java/io/github/biezhi/java8/nashorn/SuperRunner.java @@ -0,0 +1,10 @@ +package io.github.biezhi.java8.nashorn; + +public class SuperRunner implements Runnable { + + @Override + public void run() { + System.out.println("super run"); + } + +} \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn1.js b/java8-nashorn/src/main/resources/nashorn1.js new file mode 100644 index 0000000..b342405 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn1.js @@ -0,0 +1,8 @@ +var fun1 = function(name) { + print('Hi there from Javascript, ' + name); + return "greetings from javascript"; +}; + +var fun2 = function (object) { + print("JS Class Definition: " + Object.prototype.toString.call(object)); +}; \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn10.js b/java8-nashorn/src/main/resources/nashorn10.js new file mode 100644 index 0000000..9c572d5 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn10.js @@ -0,0 +1,29 @@ +var results = []; + +var Context = function () { + this.foo = 'bar'; +}; + +Context.prototype.testArgs = function () { + if (arguments[0]) { + results.push(true); + } + if (arguments[1]) { + results.push(true); + } + if (arguments[2]) { + results.push(true); + } + if (arguments[3]) { + results.push(true); + } +}; + +var testPerf = function () { + var context = new Context(); + context.testArgs(); + context.testArgs(1); + context.testArgs(1, 2); + context.testArgs(1, 2, 3); + context.testArgs(1, 2, 3, 4); +}; \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn2.js b/java8-nashorn/src/main/resources/nashorn2.js new file mode 100644 index 0000000..958ba49 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn2.js @@ -0,0 +1,35 @@ +var Nashorn2 = Java.type('io.github.biezhi.java8.nashorn.Nashorn2'); +var result = Nashorn2.fun('John Doe'); +print('\n' + result); + +Nashorn2.fun2(123); +Nashorn2.fun2(49.99); +Nashorn2.fun2(true); +Nashorn2.fun2("hi there") +Nashorn2.fun2(String("bam")) +Nashorn2.fun2(new Number(23)); +Nashorn2.fun2(new Date()); +Nashorn2.fun2(new RegExp()); +Nashorn2.fun2({foo: 'bar'}); + + +print('passing object hash:'); +Nashorn2.fun3({ + foo: 'bar', + bar: 'foo' +}); + + +print('passing custom person object:'); + +function Person(firstName, lastName) { + this.firstName = firstName; + this.lastName = lastName; + this.getFullName = function() { + return this.firstName + " " + this.lastName; + } +} + +var person1 = new Person("Peter", "Parker"); +Nashorn2.fun3(person1); +Nashorn2.fun4(person1); \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn3.js b/java8-nashorn/src/main/resources/nashorn3.js new file mode 100644 index 0000000..c58114e --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn3.js @@ -0,0 +1,121 @@ +print('------------------'); +print('IntArray:'); + +var IntArray = Java.type('int[]'); + +var array = new IntArray(5); +array[0] = 5; +array[1] = 4; +array[2] = 3; +array[3] = 2; +array[4] = 1; + +try { + array[5] = 23; +} catch (e) { + print(e.message); +} + +array[0] = "17"; +print(array[0]); + +array[0] = "wrong type"; +print(array[0]); + +array[0] = "17.3"; +print(array[0]); + +print('------------------'); + +for (var i in array) print(i); + +print('------------------'); + +for each (var val in array) print(val); + +print('------------------'); +print('ArrayList:'); + +var ArrayList = Java.type('java.util.ArrayList'); + +var list = new ArrayList(); +list.add('a'); +list.add('b'); +list.add('c'); + +for each (var el in list) print(el); + + +print('------------------'); +print('HashMap:'); + +var HashMap = Java.type('java.util.HashMap'); + +var map = new HashMap(); +map.put('foo', 'foo1'); +map.put('bar', 'bar1'); + +for each(var e in map.keySet()) print(e); + +for each(var e in map.values()) print(e); + + +print('------------------'); +print('Streams:'); + +var list2 = new ArrayList(); +list2.add("ddd2"); +list2.add("aaa2"); +list2.add("bbb1"); +list2.add("aaa1"); +list2.add("bbb3"); +list2.add("ccc"); +list2.add("bbb2"); +list2.add("ddd1"); + +list2 + .stream() + .filter(function(el) { + return el.startsWith("aaa"); + }) + .sorted() + .forEach(function(el) { + print(el); + }); + + + +print('------------------'); +print('Extend:'); + +var Runnable = Java.type('java.lang.Runnable'); +var Printer = Java.extend(Runnable, { + run: function() { + print('This was printed from a seperate thread.'); + } +}); + +var Thread = Java.type('java.lang.Thread'); +new Thread(new Printer()).start(); + +new Thread(function() { + print('this was printed from another thread'); +}).start(); + + +print('------------------'); +print('Parameter Overload:'); + +var System = Java.type('java.lang.System'); + +System.out.println(10); +System.out["println"](11.0); +System.out["println(double)"](12); + +print('------------------'); +print('JavaBeans:'); + +var Date = Java.type('java.util.Date'); +var date = new Date(); +date.year += 1900; +System.out.println(date.year); \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn4.js b/java8-nashorn/src/main/resources/nashorn4.js new file mode 100644 index 0000000..43dc139 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn4.js @@ -0,0 +1,98 @@ +// function literal with no braces + +function sqr(x) x * x; + +print(sqr(3)); + + +// for each + +var array = [1, 2, 3, 4]; +for each (var num in array) print(num); + + +// object literals in constructors + +var runnable = new java.lang.Runnable() { + run: function() { + print('on the run'); + } +}; + +runnable.run(); + + +// bind properties + +var o1 = {}; +var o2 = { foo: 'bar'}; + +Object.bindProperties(o1, o2); + +print(o1.foo); +o1.foo = 'BAM'; +print(o2.foo); + + +// string trim + +print(" hehe".trimLeft()); +print("hehe ".trimRight() + "he"); + + +// whereis +print(__FILE__, __LINE__, __DIR__); + + +// java import + +var imports = new JavaImporter(java.io, java.lang); +with (imports) { + var file = new File(__FILE__); + System.out.println(file.getAbsolutePath()); + // /path/to/my/script.js +} + + +// convert iterable to js array + +var list = new java.util.ArrayList(); +list.add("s1"); +list.add("s2"); +list.add("s3"); + +var jsArray = Java.from(list); +print(jsArray); +print(Object.prototype.toString.call(jsArray)); + + +// convert js array to java array + +var javaArray = Java.to([3, 5, 7, 11], "int[]"); +print(Object.prototype.toString.call(javaArray)); + + +// calling super + +var SuperRunner = Java.type('io.github.biezhi.java8.nashorn.SuperRunner'); +var Runner = Java.extend(SuperRunner); + +var runner = new Runner() { + run: function() { + Java.super(runner).run(); + print('on my run'); + } +} +runner.run(); + + + +// load + +load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); + +var odds = _.filter([1, 2, 3, 4, 5, 6], function (num) { + return num % 2 == 1; +}); + +print(odds); \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn5.js b/java8-nashorn/src/main/resources/nashorn5.js new file mode 100644 index 0000000..f7d0176 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn5.js @@ -0,0 +1,22 @@ +function Product(name) { + this.name = name; +} + +Product.prototype.stock = 0; +Product.prototype.price = 0; +Product.prototype.getValueOfGoods = function() { + return this.stock * this.price; +}; + +var product = new Product('Pencil'); +product.price = 4.99; +product.stock = 78; + +print('Value of Goods: ' + product.getValueOfGoods()); + + +var getValueOfGoods = function(javaProduct) { + var jsProduct = new Product(); + Object.bindProperties(jsProduct, javaProduct); + return jsProduct.getValueOfGoods(); +}; \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn6.js b/java8-nashorn/src/main/resources/nashorn6.js new file mode 100644 index 0000000..bfc8053 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn6.js @@ -0,0 +1,47 @@ +load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); +load('http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js'); + + +// simple backbone model: +// valueOfGoods will automatically be calculated when stock or price changes +var Product = Backbone.Model.extend({ + defaults: { + stock: 0, + price: 0.0, + name:'', + valueOfGoods: 0.0 + }, + + initialize: function() { + this.on('change:stock change:price', function() { + var stock = this.get('stock'); + var price = this.get('price'); + var valueOfGoods = this.getValueOfGoods(stock, price); + this.set('valueOfGoods', valueOfGoods); + }); + }, + + getValueOfGoods: function(stock, price) { + return stock * price; + } +}); + +var product = new Product(); +product.set('name', 'Pencil'); +product.set('stock', 1000); +product.set('price', 3.99); + + +// pass backbone model to java method +var Nashorn6 = Java.type('io.github.biezhi.java8.nashorn.Nashorn6'); +Nashorn6.getProduct(product.attributes); + + +// bind java object to backbone model and pass result back to java +var calculate = function(javaProduct) { + var model = new Product(); + model.set('name', javaProduct.name); + model.set('price', javaProduct.price); + model.set('stock', javaProduct.stock); + return model.attributes; +}; \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn7.js b/java8-nashorn/src/main/resources/nashorn7.js new file mode 100644 index 0000000..9246f16 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn7.js @@ -0,0 +1,27 @@ +function sqrt(x) x * x +print(sqrt(3)); + +var array = [1, 2, 3, 4]; +for each (var num in array) print(num); + +var runnable = new java.lang.Runnable() { + run: function () { + print('on the run'); + } +}; + +runnable.run(); + +var System = Java.type('java.lang.System'); +System.out["println(double)"](12); + +var Arrays = Java.type("java.util.Arrays"); +var javaArray = Java.to([2, 3, 7, 11, 14], "int[]"); + +Arrays.stream(javaArray) + .filter(function (num) { + return num % 2 === 1; + }) + .forEach(function (num) { + print(num); + }); \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn8.js b/java8-nashorn/src/main/resources/nashorn8.js new file mode 100644 index 0000000..d7bb6a6 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn8.js @@ -0,0 +1,18 @@ +var evaluate1 = function () { + (function () { + print(eval("this")); + }).call(this); +}; + +var evaluate2 = function () { + var context = {}; + (function () { + print(eval("this")); + }).call(context); +}; + +var evaluate3 = function (context) { + (function () { + print(eval("this")); + }).call(context); +}; \ No newline at end of file diff --git a/java8-nashorn/src/main/resources/nashorn9.js b/java8-nashorn/src/main/resources/nashorn9.js new file mode 100644 index 0000000..e60e2b0 --- /dev/null +++ b/java8-nashorn/src/main/resources/nashorn9.js @@ -0,0 +1,9 @@ +var size = 100000; + +var testPerf = function () { + var result = Math.floor(Math.random() * size) + 1; + for (var i = 0; i < size; i++) { + result += i; + } + return result; +}; \ No newline at end of file diff --git a/java8-optional/README.md b/java8-optional/README.md new file mode 100644 index 0000000..1338529 --- /dev/null +++ b/java8-optional/README.md @@ -0,0 +1,18 @@ +# Optional + +## Optional类的方法 + +| 方法 | 描述 | +|:-----:|:-------| +| `empty` | 返回一个空的 Optional 实例 | +| `filter` | 如果值存在并且满足提供的断言, 就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象 | +| `map` | 如果值存在,就对该值执行提供的 mapping 函数调用 | +| `flatMap` | 如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返 回一个空的 Optional 对象 | +| `get` | 如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常 | +| `ifPresent` | 如果值存在,就执行使用该值的方法调用,否则什么也不做 | +| `isPresent` | 如果值存在就返回 true,否则返回 false | +| `of` | 将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常 | +| `ofNullable` | 将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象 | +| `orElse` | 如果有值则将其返回,否则返回一个默认值 | +| `orElseGet` | 如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值 | +| `orElseThrow` | 如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常 | diff --git a/java8-optional/pom.xml b/java8-optional/pom.xml index ac9195a..b3bb564 100644 --- a/java8-optional/pom.xml +++ b/java8-optional/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-optional/src/main/java/io/github/biezhi/java8/optional/Address.java b/java8-optional/src/main/java/io/github/biezhi/java8/optional/Address.java new file mode 100644 index 0000000..405aa4d --- /dev/null +++ b/java8-optional/src/main/java/io/github/biezhi/java8/optional/Address.java @@ -0,0 +1,29 @@ +package io.github.biezhi.java8.optional; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 住址对象 + * + * @author biezhi + * @date 2018/2/11 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Address { + + /** + * 街道 + */ + private String street; + + /** + * 门牌 + */ + private String door; + +} diff --git a/java8-optional/src/main/java/io/github/biezhi/java8/optional/BeforeJava8.java b/java8-optional/src/main/java/io/github/biezhi/java8/optional/BeforeJava8.java new file mode 100644 index 0000000..c971b5b --- /dev/null +++ b/java8-optional/src/main/java/io/github/biezhi/java8/optional/BeforeJava8.java @@ -0,0 +1,37 @@ +package io.github.biezhi.java8.optional; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class BeforeJava8 { + + /** + * Java 8 之前 + * + * @param user + */ + public void saveUser(User user) { + if (null != user) { + if (null != user.getAddress()) { + // 保存 user + } + } + } + + /** + * 过多的退出语句 + * + * @param user + */ + public void saveUser2(User user) { + if (null == user) { + return; + } + if (null == user.getAddress()) { + return; + } + // 保存 user + } + +} diff --git a/java8-optional/src/main/java/io/github/biezhi/java8/optional/OptionalDemo.java b/java8-optional/src/main/java/io/github/biezhi/java8/optional/OptionalDemo.java new file mode 100644 index 0000000..1acae39 --- /dev/null +++ b/java8-optional/src/main/java/io/github/biezhi/java8/optional/OptionalDemo.java @@ -0,0 +1,100 @@ +package io.github.biezhi.java8.optional; + +import java.util.Optional; +import java.util.Properties; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class OptionalDemo { + + /** + * 1. 创建 Optional + */ + public void createOptional() { + // 声明一个空的Optional + Optional

optionalAddress = Optional.empty(); + + // 依据一个非空值创建Optional + Optional
optionalAddress2 = Optional.of(new Address()); + + // 可接受null的Optional + Optional
optionalAddress3 = Optional.ofNullable(new Address()); + } + + /** + * 2. 使用 map 从 Optional 对象中提取和转换值 + */ + public void map() { + Optional
addressOptional = Optional.ofNullable(new Address("达尔文路", "88号")); + Optional street = addressOptional.map(Address::getStreet); + } + + /** + * 3. 使用 flatMap 链接 Optional 对象 + */ + public void flatMap() { + User user = new User(); + Optional userOptional = Optional.of(user); +// userOptional.map(user -> user.getOptAddress()) + Optional stringOptional = userOptional.flatMap(User::getOptAddress).map(Address::getStreet); + + } + + /** + * 4. 默认行为及解引用 Optional 对象 + */ + public void defaultValue() { + Optional
addressOptional = Optional.ofNullable(null); + String street = addressOptional.map(Address::getStreet).orElse("北京二环"); + System.out.println(street); + } + + public static void main(String[] args) { + User user = new User(); + user.setUsername("biezhi"); + user.setPassword("123456"); + user.setOptAddress(Optional.of(new Address("达尔文路", "88号"))); + user.setAge(30); + +// Address address1 = null; +// try { +// address1 = user.getOptAddress().filter(address -> address.getDoor().contains("878")) +// .orElseThrow(new Supplier() { +// @Override +// public Throwable get() { +// return new Exception("挂了"); +// } +// }); +// } catch (Throwable throwable) { +// throwable.printStackTrace(); +// } +// System.out.println(address1); + + System.out.println(getStreet(Optional.of(user), 50)); + } + + public static String getStreet(Optional user, int minAge) { + return user.filter(u -> u.getAge() >= minAge) + .flatMap(User::getOptAddress) + .map(Address::getStreet) + .orElse("没有"); + } + + public static Optional parseInt(String value) { + try { + return Optional.ofNullable(Integer.parseInt(value)); + } catch (Exception e) { + return Optional.empty(); + } + } + + public int readPoint(Properties props, String name) { + return Optional.ofNullable(props.getProperty(name)) + .flatMap(OptionalDemo::parseInt) + .filter(i -> i > 0) + .orElse(0); + } + +} diff --git a/java8-optional/src/main/java/io/github/biezhi/java8/optional/User.java b/java8-optional/src/main/java/io/github/biezhi/java8/optional/User.java new file mode 100644 index 0000000..87b508d --- /dev/null +++ b/java8-optional/src/main/java/io/github/biezhi/java8/optional/User.java @@ -0,0 +1,23 @@ +package io.github.biezhi.java8.optional; + +import lombok.Data; + +import java.util.Optional; + +/** + * User + * + * @author biezhi + * @date 2018/2/11 + */ +@Data +public class User { + + private String username; + private String password; + private Integer age; + private Address address; + + private Optional
optAddress; + +} diff --git a/java8-proper/pom.xml b/java8-proper/pom.xml index 7c9f2e1..71c9702 100644 --- a/java8-proper/pom.xml +++ b/java8-proper/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-stream/README.md b/java8-stream/README.md new file mode 100644 index 0000000..a75f30c --- /dev/null +++ b/java8-stream/README.md @@ -0,0 +1,454 @@ +# Stream + +## 关于流 + +### 什么是流? + +流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合。 +众所周知,集合操作非常麻烦,若要对集合进行筛选、投影,需要写大量的代码,而流是以声明的形式操作集合,它就像SQL语句,我们只需告诉流需要对集合进行什么操作,它就会自动进行操作,并将执行结果交给你,无需我们自己手写代码。 +因此,流的集合操作对我们来说是透明的,我们只需向流下达命令,它就会自动把我们想要的结果给我们。由于操作过程完全由Java处理,因此它可以根据当前硬件环境选择最优的方法处理,我们也无需编写复杂又容易出错的多线程代码了。 + +### 流的特点 + +1. 只能遍历一次 + 我们可以把流想象成一条流水线,流水线的源头是我们的数据源(一个集合),数据源中的元素依次被输送到流水线上,我们可以在流水线上对元素进行各种操作。 + 一旦元素走到了流水线的另一头,那么这些元素就被“消费掉了”,我们无法再对这个流进行操作。当然,我们可以从数据源那里再获得一个新的流重新遍历一遍。 +2. 采用内部迭代方式 + 若要对集合进行处理,则需我们手写处理代码,这就叫做外部迭代。 + 而要对流进行处理,我们只需告诉流我们需要什么结果,处理过程由流自行完成,这就称为内部迭代。 + +### 流的操作种类 + +流的操作分为两种,分别为中间操作和终端操作。 + +1. 中间操作 + 当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。 + 中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。 +2. 终端操作 + 当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终端操作。 + 终端操作将返回一个执行结果,这就是你想要的数据。 + +### 流的操作过程 + +使用流一共需要三步: + +1. 准备一个数据源 +2. 执行中间操作 + 中间操作可以有多个,它们可以串连起来形成流水线。 +3. 执行终端操作 + 执行终端操作后本次流结束,你将获得一个执行结果。 + +## 使用流 + +### 创建流 + +在使用流之前,首先需要拥有一个数据源,并通过StreamAPI提供的一些方法获取该数据源的流对象。数据源可以有多种形式: + +**1. 集合** + +这种数据源较为常用,通过stream()方法即可获取流对象: + +```java +List list = new ArrayList(); +Stream stream = list.stream(); +``` + +**2. 数组** + +通过Arrays类提供的静态函数stream()获取数组的流对象: + +```java +String[] names = {"chaimm","peter","john"}; +Stream stream = Arrays.stream(names); +``` + +**3. 值** + +直接将几个值变成流对象: + +```java +Stream stream = Stream.of("chaimm","peter","john"); +``` + +**4. 文件** + +```java +try(Stream lines = Files.lines(Paths.get(“文件路径名”),Charset.defaultCharset())){ + //可对lines做一些操作 +}catch(IOException e){ +} +``` + +**5. iterator** + +**创建无限流** + +```java +Stream.iterate(0, n -> n + 2) + .limit(10) + .forEach(System.out::println); +``` + +> PS:Java7简化了IO操作,把打开IO操作放在try后的括号中即可省略关闭IO的代码。 + +### 筛选 filter + +filter 函数接收一个Lambda表达式作为参数,该表达式返回boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为true的元素。 +如,筛选出所有学生: + +```java +List result = list.stream() + .filter(Person::isStudent) + .collect(toList()); +``` + +### 去重distinct + +去掉重复的结果: + +```java +List result = list.stream() + .distinct() + .collect(toList()); +``` + +### 截取 + +截取流的前N个元素: + +```java +List result = list.stream() + .limit(3) + .collect(toList()); +``` + +### 跳过 + +跳过流的前n个元素: + +```java +List result = list.stream() + .skip(3) + .collect(toList()); +``` + +### 映射 + +对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。 +如,获取每个人的姓名(实则是将Perosn类型转换成String类型): + +```java +List result = list.stream() + .map(Person::getName) + .collect(toList()); +``` + +### 合并多个流 + +例:列出List中各不相同的单词,List集合如下: + +```java +List list = new ArrayList(); +list.add("I am a boy"); +list.add("I love the girl"); +list.add("But the girl loves another girl"); +``` + +思路如下: + +首先将list变成流: + +```java +list.stream(); +``` + +按空格分词: + +```java +list.stream() + .map(line->line.split(" ")); +``` + +分完词之后,每个元素变成了一个String[]数组。 + +将每个 `String[]` 变成流: + +```java +list.stream() + .map(line->line.split(" ")) + .map(Arrays::stream) +``` + +此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流。 + +将小流合并成一个大流:用 `flatMap` 替换刚才的 map + +```java +list.stream() + .map(line->line.split(" ")) + .flatMap(Arrays::stream) +``` + +去重 + +```java +list.stream() + .map(line->line.split(" ")) + .flatMap(Arrays::stream) + .distinct() + .collect(toList()); +``` + +### 是否匹配任一元素:anyMatch + +anyMatch用于判断流中是否存在至少一个元素满足指定的条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。 +如,判断list中是否有学生: + +```java +boolean result = list.stream() + .anyMatch(Person::isStudent); +``` + +### 是否匹配所有元素:allMatch + +allMatch用于判断流中的所有元素是否都满足指定条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。 +如,判断是否所有人都是学生: + +```java +boolean result = list.stream() + .allMatch(Person::isStudent); +``` + +### 是否未匹配所有元素:noneMatch + +noneMatch与allMatch恰恰相反,它用于判断流中的所有元素是否都不满足指定条件: + +```java +boolean result = list.stream() + .noneMatch(Person::isStudent); +``` + +### 获取任一元素findAny + +findAny能够从流中随便选一个元素出来,它返回一个Optional类型的元素。 + +```java +Optional person = list.stream().findAny(); +``` + +### 获取第一个元素findFirst + +```java +Optional person = list.stream().findFirst(); +``` + +### 归约 + +归约是将集合中的所有元素经过指定运算,折叠成一个元素输出,如:求最值、平均数等,这些操作都是将一个集合的元素折叠成一个元素输出。 + +在流中,reduce函数能实现归约。 +reduce函数接收两个参数: + +1. 初始值 +2. 进行归约操作的Lambda表达式 + +**元素求和:自定义Lambda表达式实现求和** + +例:计算所有人的年龄总和 + +```java +int age = list.stream().reduce(0, (person1,person2)->person1.getAge()+person2.getAge()); +``` + +1. reduce的第一个参数表示初试值为0; +2. reduce的第二个参数为需要进行的归约操作,它接收一个拥有两个参数的Lambda表达式,reduce会把流中的元素两两输给Lambda表达式,最后将计算出累加之和。 + +**元素求和:使用Integer.sum函数求和** + +上面的方法中我们自己定义了Lambda表达式实现求和运算,如果当前流的元素为数值类型,那么可以使用Integer提供了sum函数代替自定义的Lambda表达式,如: + +```java +int age = list.stream().reduce(0, Integer::sum); +``` + +Integer类还提供了 `min`、`max` 等一系列数值操作,当流中元素为数值类型时可以直接使用。 + +### 数值流的使用 + +采用reduce进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低。 +当流操作为纯数值操作时,使用数值流能获得较高的效率。 + +**将普通流转换成数值流** + +StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。 +如,将Person中的age转换成数值流: + +```java +IntStream stream = list.stream().mapToInt(Person::getAge); +``` + +**数值计算** + +每种数值流都提供了数值计算函数,如max、min、sum等。如,找出最大的年龄: + +```java +OptionalInt maxAge = list.stream() + .mapToInt(Person::getAge) + .max(); +``` + +由于数值流可能为空,并且给空的数值流计算最大值是没有意义的,因此max函数返回OptionalInt,它是Optional的一个子类,能够判断流是否为空,并对流为空的情况作相应的处理。 +此外,mapToInt、mapToDouble、mapToLong进行数值操作后的返回结果分别为:OptionalInt、OptionalDouble、OptionalLong + +## 中间操作和收集操作 + +| 操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 | +|:-----:|:--------|:-------|:-------|:-------| +| `filter` | 中间 | `Stream` | `Predicate` | `T -> boolean` | +| `distinct` | 中间 | `Stream` | | | +| `skip` | 中间 | `Stream` | long | | +| `map` | 中间 | `Stream` | `Function` | `T -> R` | +| `flatMap` | 中间 | `Stream` | `Function>` | `T -> Stream` | +| `limit` | 中间 | `Stream` | long | | +| `sorted` | 中间 | `Stream` | `Comparator` | `(T, T) -> int` | +| `anyMatch` | 终端 | `boolean` | `Predicate` | `T -> boolean` | +| `noneMatch` | 终端 | `boolean` | `Predicate` | `T -> boolean` | +| `allMatch` | 终端 | `boolean` | `Predicate` | `T -> boolean` | +| `findAny` | 终端 | `Optional` | | | +| `findFirst` | 终端 | `Optional` | | | +| `forEach` | 终端 | `void` | `Consumer` | `T -> void` | +| `collect` | 终端 | `R` | `Collector` | | +| `reduce` | 终端 | `Optional` | `BinaryOperator` | `(T, T) -> T` | +| `count` | 终端 | `long` | | | + +## Collector 收集 + +收集器用来将经过筛选、映射的流进行最后的整理,可以使得最后的结果以不同的形式展现。 +`collect` 方法即为收集器,它接收 `Collector` 接口的实现作为具体收集器的收集方法。 +`Collector` 接口提供了很多默认实现的方法,我们可以直接使用它们格式化流的结果;也可以自定义 `Collector` 接口的实现,从而定制自己的收集器。 + +### 归约 + +流由一个个元素组成,归约就是将一个个元素“折叠”成一个值,如求和、求最值、求平均值都是归约操作。 + +### 一般性归约 + +若你需要自定义一个归约操作,那么需要使用 `Collectors.reducing` 函数,该函数接收三个参数: + +* 第一个参数为归约的初始值 +* 第二个参数为归约操作进行的字段 +* 第三个参数为归约操作的过程 + +## 汇总 + +Collectors类专门为汇总提供了一个工厂方法:`Collectors.summingInt`。 +它可接受一 个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通的 `collect` 方法后即执行我们需要的汇总操作。 + +### 分组 + +数据分组是一种更自然的分割数据操作,分组就是将流中的元素按照指定类别进行划分,类似于SQL语句中的 `GROUPBY`。 + +### 多级分组 + +多级分组可以支持在完成一次分组后,分别对每个小组再进行分组。 +使用具有两个参数的 `groupingBy` 重载方法即可实现多级分组。 + +* 第一个参数:一级分组的条件 +* 第二个参数:一个新的 `groupingBy` 函数,该函数包含二级分组的条件 + +**Collectors 类的静态工厂方法** + +| 工厂方法 | 返回类型 | 用途 | 示例 | +|:-----:|:--------|:-------|:-------| +| `toList` | `List` | 把流中所有项目收集到一个 List | `List projects = projectStream.collect(toList());` | +| `toSet` | `Set` | 把流中所有项目收集到一个 Set,删除重复项 | `Set projects = projectStream.collect(toSet());` | +| `toCollection` | `Collection` | 把流中所有项目收集到给定的供应源创建的集合 | `Collection projects = projectStream.collect(toCollection(), ArrayList::new);` | +| `counting` | `Long` | 计算流中元素的个数 | `long howManyProjects = projectStream.collect(counting());` | +| `summingInt` | `Integer` | 对流中项目的一个整数属性求和 | `int totalStars = projectStream.collect(summingInt(Project::getStars));` | +| `averagingInt` | `Double` | 计算流中项目 Integer 属性的平均值 | `double avgStars = projectStream.collect(averagingInt(Project::getStars));` | +| `summarizingInt` | `IntSummaryStatistics` | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 | `IntSummaryStatistics projectStatistics = projectStream.collect(summarizingInt(Project::getStars));` | +| `joining` | `String` | 连接对流中每个项目调用 toString 方法所生成的字符串 | `String shortProject = projectStream.map(Project::getName).collect(joining(", "));` | +| `maxBy` | `Optional` | 按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(maxBy(comparingInt(Project::getStars)));` | +| `minBy` | `Optional` | 按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(minBy(comparingInt(Project::getStars)));` | +| `reducing` | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中的元素逐个结合,从而将流归约为单个值 | `int totalStars = projectStream.collect(reducing(0, Project::getStars, Integer::sum));` | +| `collectingAndThen` | 转换函数返回的类型 | 包含另一个收集器,对其结果应用转换函数 | `int howManyProjects = projectStream.collect(collectingAndThen(toList(), List::size));` | +| `groupingBy` | `Map>` | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 | `Map> projectByLanguage = projectStream.collect(groupingBy(Project::getLanguage));` | +| `partitioningBy` | `Map>` | 根据对流中每个项目应用断言的结果来对项目进行分区 | `Map> vegetarianDishes = projectStream.collect(partitioningBy(Project::isVegetarian));` | + +### 转换类型 + +有一些收集器可以生成其他集合。比如前面已经见过的 `toList`,生成了 `java.util.List` 类的实例。 +还有 `toSet` 和 `toCollection`,分别生成 `Set` 和 `Collection` 类的实例。 +到目前为止, 我已经讲了很多流上的链式操作,但总有一些时候,需要最终生成一个集合——比如: + +- 已有代码是为集合编写的,因此需要将流转换成集合传入; +- 在集合上进行一系列链式操作后,最终希望生成一个值; +- 写单元测试时,需要对某个具体的集合做断言。 + +使用 `toCollection`,用定制的集合收集元素 + +```java +stream.collect(toCollection(TreeSet::new)); +``` + +还可以利用收集器让流生成一个值。 `maxBy` 和 `minBy` 允许用户按某种特定的顺序生成一个值。 + +### 数据分区 + +分区是分组的特殊情况:由一个断言(返回一个布尔值的函数)作为分类函数,它称分区函数。 +分区函数返回一个布尔值,这意味着得到的分组 `Map` 的键类型是 `Boolean`,于是它最多可以分为两组: true是一组,false是一组。 + +分区的好处在于保留了分区函数返回true或false的两套流元素列表。 + +### 并行流 + +并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。 + +将一个顺序执行的流转变成一个并发的流只要调用 `parallel()` 方法 + +```java +public static long parallelSum(long n){ + return Stream.iterate(1L, i -> i +1).limit(n).parallel().reduce(0L,Long::sum); +} +``` + +将一个并发流转成顺序的流只要调用 `sequential()` 方法 + +```java +stream.parallel().filter(...).sequential().map(...).parallel().reduce(); +``` + +这两个方法可以多次调用,只有最后一个调用决定这个流是顺序的还是并发的。 + +并发流使用的默认线程数等于你机器的处理器核心数。 + +通过这个方法可以修改这个值,这是全局属性。 + +```java +System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12"); +``` + +并非使用多线程并行流处理数据的性能一定高于单线程顺序流的性能,因为性能受到多种因素的影响。 +如何高效使用并发流的一些建议: + +1. 如果不确定, 就自己测试。 +2. 尽量使用基本类型的流 IntStream, LongStream, DoubleStream +3. 有些操作使用并发流的性能会比顺序流的性能更差,比如limit,findFirst,依赖元素顺序的操作在并发流中是极其消耗性能的。findAny的性能就会好很多,应为不依赖顺序。 +4. 考虑流中计算的性能(Q)和操作的性能(N)的对比, Q表示单个处理所需的时间,N表示需要处理的数量,如果Q的值越大, 使用并发流的性能就会越高。 +5. 数据量不大时使用并发流,性能得不到提升。 +6. 考虑数据结构:并发流需要对数据进行分解,不同的数据结构被分解的性能时不一样的。 + +**流的数据源和可分解性** + +| 源 | 可分解性 | +|:-----:|:-------| +| `ArrayList` | 非常好 | +| `LinkedList` | 差 | +| `IntStream.range` | 非常好 | +| `Stream.iterate` | 差 | +| `HashSet` | 好 | +| `TreeSet` | 好 | + +**流的特性以及中间操作对流的修改都会对数据对分解性能造成影响。 比如固定大小的流在任务分解的时候就可以平均分配,但是如果有filter操作,那么流就不能预先知道在这个操作后还会剩余多少元素。** + +**考虑终端操作的性能:如果终端操作在合并并发流的计算结果时的性能消耗太大,那么使用并发流提升的性能就会得不偿失。** diff --git a/java8-stream/pom.xml b/java8-stream/pom.xml index b76e05e..1f87fa3 100644 --- a/java8-stream/pom.xml +++ b/java8-stream/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - lets-java8 + learn-java8 io.github.biezhi 1.0-SNAPSHOT diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/Project.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/Project.java new file mode 100644 index 0000000..0d1281a --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/Project.java @@ -0,0 +1,70 @@ +package io.github.biezhi.java8.stream; + +import lombok.Builder; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 项目 + * + * @author biezhi + * @date 2018/2/9 + */ +@Data +@Builder +public class Project { + + /** + * 项目名称 + */ + private String name; + + /** + * 编程语言 + */ + private String language; + + /** + * star 数 + */ + private Integer stars; + + /** + * 描述 + */ + private String description; + + /** + * 作者 + */ + private String author; + + /** + * fork数 + */ + private Integer forks; + + public static List buildData(){ + List data = new ArrayList<>(); + + data.add(Project.builder().name("Blade").language("java").author("biezhi") + .stars(3500).forks(2000).description("Lightning fast and elegant mvc framework for Java8").build()); + + data.add(Project.builder().name("Tale").language("javascript").author("biezhi") + .stars(2600).forks(2300).description("Best beautiful java blog, worth a try").build()); + + data.add(Project.builder().name("Vue.js").language("js").author("yyx990803") + .stars(83000).forks(10322).description("A progressive, incrementally-adoptable JavaScript framework for building UI on the web.").build()); + + data.add(Project.builder().name("Flask").language("python").author("pallets") + .stars(10500).forks(3000).description("The Python micro framework for building web applications").build()); + + data.add(Project.builder().name("Elves").language("java").author("biezhi") + .stars(200).forks(100).description("Spider").build()); + + return data; + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java7.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java7.java new file mode 100644 index 0000000..8bafde2 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java7.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.stream.lesson1; + +import io.github.biezhi.java8.stream.Project; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class Java7 { + + public static void main(String[] args) { + List result = new ArrayList<>(); + + List projects = new ArrayList<>(); + for (Project project : projects) { + if(project.getStars() > 1000){ + result.add(project); + } + } + Collections.sort(projects, new Comparator() { + @Override + public int compare(Project o1, Project o2) { + return o1.getStars().compareTo(o2.getStars()); + } + }); + + List names = new ArrayList<>(); + + for (Project project : projects) { + names.add(project.getName()); + } + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java8.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java8.java new file mode 100644 index 0000000..c4bedd2 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson1/Java8.java @@ -0,0 +1,32 @@ +package io.github.biezhi.java8.stream.lesson1; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author biezhi + * @date 2018/2/11 + */ +public class Java8 { + + public static void main(String[] args) { + List projects = Project.buildData(); + List names = projects.stream() + .filter(p -> { + System.out.println(p.getName()); + return p.getStars() > 1000; + }) + .map(p -> { + System.out.println(p.getName()); + return p.getName(); + }) + .limit(3) + .collect(Collectors.toList()); + System.out.println(names); + + names.stream().forEach(name-> System.out.println(name)); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example1.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example1.java new file mode 100644 index 0000000..93fcdbb --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example1.java @@ -0,0 +1,30 @@ +package io.github.biezhi.java8.stream.lesson2; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +/** + * 创建流 + *

+ * Stream.of + * Arrays.stream + * collection.stream + * Files.lines + * Stream.iterate + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example1 { + + public static void main(String[] args) { + List list = Arrays.asList("hello", "world"); + Stream stream = list.stream(); + + Stream stringStream = Arrays.stream(new String[]{"hello", "world"}); + + Stream stream1 = Stream.of("hello", "world"); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example2.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example2.java new file mode 100644 index 0000000..91fc817 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example2.java @@ -0,0 +1,37 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 筛选元素 + * + * filter 使用 + * distinct 使用 + * limit 使用 + * skip 使用 + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example2 { + + public static void main(String[] args) { + List projects = Project.buildData(); + + List collect = projects.stream() + .filter(project -> project.getStars() > 1000) + .collect(Collectors.toList()); + + // distinct + Stream numbers = Stream.of(1, 2, 3, 3, 2, 4); + numbers.distinct().limit(3).forEach(n -> System.out.println(n)); + + System.out.println("==================="); + Stream.of(1, 2, 3, 3, 2, 4).skip(4).forEach(n -> System.out.println(n)); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example3.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example3.java new file mode 100644 index 0000000..3c6e6ca --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example3.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.stream.lesson2; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 映射 + *

+ * map 使用 + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example3 { + + public static void main(String[] args) { + List words = Arrays.asList("Java 8", "Lambdas", "In", "Action"); + + words.stream() + .map(word -> word.length()) + .collect(Collectors.toList()) + .forEach(i -> System.out.println(i)); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example4.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example4.java new file mode 100644 index 0000000..78298b2 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example4.java @@ -0,0 +1,29 @@ +package io.github.biezhi.java8.stream.lesson2; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 扁平流 flatMap + *

+ * 列出List中各不相同的单词 + *

+ * + * @author biezhi + * @date 2018/2/12 + */ +public class Example4 { + + public static void main(String[] args) { + List list = Arrays.asList("I am a boy", "I love the girl", "But the girl loves another girl"); + + list.stream() + .map(word -> word.split(" ")) // Stream + .flatMap(Arrays::stream) + .distinct() + .collect(Collectors.toList()); + + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example5.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example5.java new file mode 100644 index 0000000..da0f7d1 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example5.java @@ -0,0 +1,39 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; + +/** + * 匹配元素 + *

+ * allMatch 使用 + * anyMatch 使用 + * noneMatch 使用 + * findFirst 使用 + * findAny 使用 + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example5 { + + public static void main(String[] args) { + List projects = Project.buildData(); + + boolean hasBiezhi = projects.stream() + .anyMatch(p -> p.getAuthor().equals("biezhi")); + + System.out.println(hasBiezhi); + + System.out.println(projects.stream() + .allMatch(p -> p.getAuthor().equals("biezhi"))); + + System.out.println(projects.stream() + .noneMatch(p -> p.getAuthor().equals("biezhi"))); + + System.out.println(projects.stream().findAny().get()); + System.out.println(projects.stream().findFirst().get()); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example6.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example6.java new file mode 100644 index 0000000..952aa4b --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example6.java @@ -0,0 +1,35 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.Arrays; +import java.util.List; + +/** + * 归约(reduce) + *

+ * 1.元素求和 + * 2. + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example6 { + + public static void main(String[] args) { + List projects = Project.buildData(); + List numbers = Arrays.asList(2, 4, 5, 6); + + int sum = 0; + for (int x : numbers) { + sum += x; + } + + System.out.println(sum); + + Integer reduce = numbers.stream() + .reduce(0, (a, b) -> a + b); + System.out.println(reduce); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example7.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example7.java new file mode 100644 index 0000000..a0773f1 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Example7.java @@ -0,0 +1,26 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.OptionalInt; + +/** + * 数值流 + *

+ * IntStream、DoubleStream、LongStream + * + * @author biezhi + * @date 2018/2/12 + */ +public class Example7 { + + public static void main(String[] args) { + List projects = Project.buildData(); + OptionalInt max = projects.stream() + .mapToInt(p -> p.getStars()) + .max(); + System.out.println(max.getAsInt()); + } + +} \ No newline at end of file diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz1.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz1.java new file mode 100644 index 0000000..2a870f9 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz1.java @@ -0,0 +1,38 @@ +package io.github.biezhi.java8.stream.lesson2; + +import lombok.AllArgsConstructor; + +import java.util.stream.Stream; + +/** + * 3. 斐波纳契元组序列 + *

+ * 斐波纳契数列是著名的经典编程练习。 + * 下面这个数列就是斐波纳契数列的一部分: + * 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55…数列中开始的两个数字是 0 和 1,后续的每个数字都是前两个数字之和。 + *

+ * 斐波纳契元组序列与此类似,是数列中数字和其后续数字组成的元组构成的序列: + * (0, 1), (1, 1), (1, 2), (2, 3), (3, 5), (5, 8), (8, 13), (13, 21) … + * 你的任务是用iterate方法生成斐波纳契元组序列中的前20个元素。 + * + * @author biezhi + * @date 2018/2/12 + */ +public class Quiz1 { + + @AllArgsConstructor + static class Tuple{ + int first; + int second; + } + + public static void main(String[] args) { + // tuple = (0, 1) + // next [0] = prev tuple [1] + // next [1] = prev (tuple [0] + tuple[1]) + Stream.iterate(new Tuple(0, 1), tuple -> new Tuple(tuple.second, tuple.first + tuple.second)) + .limit(20) + .forEach(tuple -> System.out.println("("+ tuple.first +","+ tuple.second +")")); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz2.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz2.java new file mode 100644 index 0000000..5fba85d --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz2.java @@ -0,0 +1,24 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 1. 你将如何利用流来筛选前两个Java项目呢? + *

+ * + * @author biezhi + * @date 2018/2/12 + */ +public class Quiz2 { + + public static void main(String[] args) { + List projects = Project.buildData(); + + System.out.println(projects.stream().map(Project::getName).limit(2).collect(Collectors.toList())); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz3.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz3.java new file mode 100644 index 0000000..b3ff4c1 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz3.java @@ -0,0 +1,58 @@ +package io.github.biezhi.java8.stream.lesson2; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 2. + *

+ * 2.1 给定一个数字列表,如何返回一个由每个数的平方构成的列表呢? + * 例如,给定[1, 2, 3, 4, 5],应该返回[1, 4, 9, 16, 25] + *

+ * 2.2 给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4], + * 应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。 + * 为简单起见,你可以用有两个元素的数组来代表数对。 + *

+ * 2.3 如何扩展前一个例子,只返回总和能被3整除的数对呢?例如(2, 4)和(3, 3)是可以的。 + * + * @author biezhi + * @date 2018/2/12 + */ +public class Quiz3 { + + private static void q1() { + List numbers = Arrays.asList(1, 2, 3, 4, 5); + + numbers.stream() + .map(n -> n * n) + .forEach(n -> System.out.println(n)); + } + + private static void q2() { + List numbers1 = Arrays.asList(1, 2, 3); + List numbers2 = Arrays.asList(3, 4); + + List pairs = numbers1.stream() + .flatMap(i -> numbers2.stream().map(j -> new int[]{i, j})) + .collect(Collectors.toList()); + pairs.forEach(pair -> System.out.println(Arrays.toString(pair))); + } + + private static void q3() { + List numbers1 = Arrays.asList(1, 2, 3); + List numbers2 = Arrays.asList(3, 4); + + List pairs = numbers1.stream() + .flatMap(i -> numbers2.stream().filter(j -> (i + j) % 3 == 0).map(j -> new int[]{i, j})) + .collect(Collectors.toList()); + pairs.forEach(pair -> System.out.println(Arrays.toString(pair))); + } + + public static void main(String[] args) { +// q1(); +// q2(); + q3(); + } + +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz4.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz4.java new file mode 100644 index 0000000..aa2d014 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/Quiz4.java @@ -0,0 +1,25 @@ +package io.github.biezhi.java8.stream.lesson2; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; + +/** + * 4. 请使用reduce计算 biezhi 的项目有多少 star + * + * @author biezhi + * @date 2018/2/12 + */ +public class Quiz4 { + + public static void main(String[] args) { + + List projects = Project.buildData(); + Integer biezhi = projects.stream() + .filter(p -> p.getAuthor().equals("biezhi")) + .map(p -> p.getStars()) + .reduce(0, Integer::sum); + System.out.println(biezhi); + } + +} \ No newline at end of file diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/QuizEnd.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/QuizEnd.java new file mode 100644 index 0000000..bdb59f0 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson2/QuizEnd.java @@ -0,0 +1,110 @@ +package io.github.biezhi.java8.stream.lesson2; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * (1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。 + * (2) 交易员都在哪些不同的城市工作过? + * (3) 查找所有来自于剑桥的交易员,并按姓名排序。 + * (4) 返回所有交易员的姓名字符串,按字母顺序排序。 + * (5) 有没有交易员是在米兰工作的? + * (6) 打印生活在剑桥的交易员的所有交易额。 + * (7) 所有交易中,最高的交易额是多少? + * (8) 找到交易额最小的交易。 + * + * @author biezhi + * @date 2018/2/12 + */ +public class QuizEnd { + + // 交易员 + @Data + @AllArgsConstructor + static class Trader { + // 姓名 + private String name; + // 城市 + private String city; + } + + // 交易 + @Data + @AllArgsConstructor + static class Transaction { + private Trader trader; + // 交易年份 + private int year; + // 交易额 + private int value; + } + + public static void main(String[] args) { + Trader raoul = new Trader("Raoul", "Cambridge"); + Trader mario = new Trader("Mario", "Milan"); + Trader alan = new Trader("Alan", "Cambridge"); + Trader brian = new Trader("Brian", "Cambridge"); + + List transactions = Arrays.asList( + new Transaction(brian, 2011, 300), + new Transaction(raoul, 2012, 1000), + new Transaction(raoul, 2011, 400), + new Transaction(mario, 2012, 710), + new Transaction(mario, 2012, 700), + new Transaction(alan, 2012, 950) + ); + + // 01 + transactions.stream() + .filter(transaction -> transaction.year == 2011) + .sorted(Comparator.comparing(Transaction::getValue)) + .collect(Collectors.toList()); + + // 02 + transactions.stream() + .map(Transaction::getTrader) + .map(Trader::getCity) + .distinct() + .collect(Collectors.toList()); + + // 03 + transactions.stream() + .map(Transaction::getTrader) + .filter(trader -> trader.getCity().equals("Cambridge")) + .sorted(Comparator.comparing(Trader::getName)) + .collect(Collectors.toList()); + + // 04 + transactions.stream() + .map(Transaction::getTrader) + .map(Trader::getName) + .sorted(Comparator.naturalOrder()) + .collect(Collectors.toList()); + + // 05 + transactions.stream() + .map(Transaction::getTrader) + .anyMatch(trader -> trader.getCity().equals("Milan")); + + // 06 + transactions.stream() + .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge")) + .map(Transaction::getValue) + .reduce(0, Integer::sum); + + // 07 + transactions.stream() + .mapToInt(Transaction::getValue) + .max(); + + // 08 + transactions.stream() + .reduce((t1, t2) -> t1.getValue() < t2.getValue() ? t1 : t2); + } + +} \ No newline at end of file diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example1.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example1.java new file mode 100644 index 0000000..5f9faf0 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example1.java @@ -0,0 +1,55 @@ +package io.github.biezhi.java8.stream.lesson3; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.*; + +/** + * 1. 计数 + *

+ * Collectors.counting + * count + *

+ * 2. 最值 + * Collectors.maxBy + *

+ * 3. 求和 + * Collectors.summingInt + *

+ * 4. 求平均值 + * Collectors.averagingInt + *

+ * 5. 连接字符串 + * Collectors.joining + *

+ * 6. 一般归约 + * Collectors.reducing + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example1 { + + public static void main(String[] args) { + List projects = Project.buildData(); + Double collect = projects.stream() + .collect(averagingInt(Project::getStars)); + System.out.println(collect); + + System.out.println(Stream.of("Hello", "Java8") + .collect(joining(","))); + + Integer collect1 = projects.stream() + .collect(reducing(0, Project::getStars, (x, y) -> x + y)); + System.out.println(collect1); + + Optional collect2 = projects.stream() + .map(Project::getStars) + .collect(reducing((x, y) -> x + y)); + System.out.println(collect2); + } +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example2.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example2.java new file mode 100644 index 0000000..b120543 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example2.java @@ -0,0 +1,32 @@ +package io.github.biezhi.java8.stream.lesson3; + +import io.github.biezhi.java8.stream.Project; +import static java.util.stream.Collectors.*; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 将结果收集到 Map 中 + *

+ * Collectors.toMap + * Function.identity() + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example2 { + + public static void main(String[] args) { + List projects = Project.buildData(); + + Map collect = projects.stream() + .collect(toMap(Project::getName, Project::getStars)); + System.out.println(collect); + + Map collect1 = projects.stream() + .collect(toMap(Project::getName, Function.identity())); + System.out.println(collect1); + } +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example3.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example3.java new file mode 100644 index 0000000..f659406 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example3.java @@ -0,0 +1,40 @@ +package io.github.biezhi.java8.stream.lesson3; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.*; + +/** + * 根据作者名进行分组 + *

+ * Collectors.groupingBy + *

+ * 然后根据编程语言类型做前后端分组 + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example3 { + + public static void main(String[] args) { + List projects = Project.buildData(); + Map> collect = projects.stream() + .collect(groupingBy(Project::getAuthor)); + System.out.println(collect); + + Map> collect1 = projects.stream() + .collect(groupingBy(Project::getAuthor, + groupingBy(p -> { + if ("java".equalsIgnoreCase(p.getLanguage()) || + "python".equalsIgnoreCase(p.getLanguage())) { + return "后端"; + } + return "前端"; + }, counting()) + )); + System.out.println(collect1); + } +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example4.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example4.java new file mode 100644 index 0000000..df27ad9 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example4.java @@ -0,0 +1,33 @@ +package io.github.biezhi.java8.stream.lesson3; + +import io.github.biezhi.java8.stream.Project; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.*; + +/** + * 数据分区 + *

+ * Collectors.partitioningBy + *

+ * 根据前后端将项目分为两组 + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example4 { + + public static boolean isBackEnd(Project project){ + return "java".equalsIgnoreCase(project.getLanguage()) || "python".equalsIgnoreCase(project.getLanguage()); + } + + public static void main(String[] args) { + List projects = Project.buildData(); + + Map> collect = projects.stream() + .collect(partitioningBy(Example4::isBackEnd)); + System.out.println(collect); + } +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example5.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example5.java new file mode 100644 index 0000000..d6691d8 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example5.java @@ -0,0 +1,42 @@ +package io.github.biezhi.java8.stream.lesson3; + +import io.github.biezhi.java8.stream.Project; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +import static java.util.stream.Collectors.*; + +/** + * 转换类型 + *

+ * Collectors.toCollection + *

+ * Collectors.collectingAndThen + *

+ * Collectors.maxBy + *

+ * Collectors.minBy + *

+ * 按照作者名称筛选出每组star最高的项目 + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example5 { + + public static void main(String[] args) { + List projects = Project.buildData(); + + Collection collect = projects.stream() + .collect(toCollection(CopyOnWriteArrayList::new)); + System.out.println(collect); + + Map collect1 = projects.stream() + .collect(groupingBy(Project::getAuthor, collectingAndThen( + maxBy(Comparator.comparingInt(Project::getStars)), + Optional::get + ))); + System.out.println(collect1); + } +} diff --git a/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example6.java b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example6.java new file mode 100644 index 0000000..521c839 --- /dev/null +++ b/java8-stream/src/main/java/io/github/biezhi/java8/stream/lesson3/Example6.java @@ -0,0 +1,57 @@ +package io.github.biezhi.java8.stream.lesson3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * 并行流 + * + * @author biezhi + * @date 2018/3/2 + */ +public class Example6 { + + public static void main(String[] args) { +// int max = 1000000; +// List values = new ArrayList<>(max); +// for (int i = 0; i < max; i++) { +// UUID uuid = UUID.randomUUID(); +// values.add(uuid.toString()); +// } +// +// long t0 = System.nanoTime(); +// long count = values.parallelStream().sorted().count(); +// System.out.println(count); +// long t1 = System.nanoTime(); +// long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); +// System.out.println(String.format("sequential sort took: %d ms", millis)); +// +// List list = Arrays.asList(1, 3, 4, 2, 9); +// list.stream() +// .parallel(); + + Arrays.asList("a1", "a2", "b1", "c2", "c1") + .parallelStream() + .filter(s -> { + System.out.format("filter: %s [%s]\n", + s, Thread.currentThread().getName()); + return true; + }) + .map(s -> { + System.out.format("map: %s [%s]\n", + s, Thread.currentThread().getName()); + return s.toUpperCase(); + }) + .sorted((s1, s2) -> { + System.out.format("sort: %s <> %s [%s]\n", + s1, s2, Thread.currentThread().getName()); + return s1.compareTo(s2); + }) + .forEach(s -> System.out.format("forEach: %s [%s]\n", + s, Thread.currentThread().getName())); + + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index ae97d59..5c15e63 100644 --- a/pom.xml +++ b/pom.xml @@ -5,10 +5,33 @@ 4.0.0 io.github.biezhi - lets-java8 + learn-java8 pom 1.0-SNAPSHOT + + java8-lambda + java8-optional + java8-stream + java8-default-methods + java8-datetime-api + java8-nashorn + java8-best-practice + java8-proper + java8-concurrent + java8-growing + java8-completablefuture + + + + + org.projectlombok + lombok + 1.16.18 + provided + + + @@ -21,16 +44,5 @@ - - java8-lambda - java8-optional - java8-stream - java8-default-methods - java8-datetime-api - java8-nashorn - java8-best-practice - java8-proper - java8-concurrent - \ No newline at end of file