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 extends String> 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 extends String> 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