diff --git a/README.md b/README.md index 1045be5..1c21ea6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,13 @@ ----- +## 赞助 + +如果您觉得该项目对您有帮助,请扫描下方二维码对我进行鼓励,以便我更好的维护和更新,谢谢支持! + +![支付宝](https://brianway.github.io/img/alipay_small.png) +![微信](https://brianway.github.io/img/wechatpay_small.png) + # TODO 计划将这个仓库进行重构,逐步扩充并实现下面的功能。 @@ -66,7 +73,7 @@ * [ ] 容器类部分使用模块 java-container * [ ] IO 部分使用模块 java-io * [x] Java 虚拟机相关部分使用模块 java-jvm(2017.3.20 完成雏形) -* [ ] Java 8 新特性使用模块 java8(正在进行) +* [x] Java 8 新特性使用模块 java8(2017.3.29 完成) ----- diff --git "a/blogs/img/javaSE_\344\273\243\347\220\206\346\236\266\346\236\204\345\233\276.png" "b/blogs/img/javaSE_\344\273\243\347\220\206\346\236\266\346\236\204\345\233\276.png" new file mode 100644 index 0000000..ae378bb Binary files /dev/null and "b/blogs/img/javaSE_\344\273\243\347\220\206\346\236\266\346\236\204\345\233\276.png" differ diff --git "a/blogs/img/javaSE_\345\212\250\346\200\201\344\273\243\347\220\206\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\233\276.jpg" "b/blogs/img/javaSE_\345\212\250\346\200\201\344\273\243\347\220\206\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\233\276.jpg" new file mode 100644 index 0000000..7b2819d Binary files /dev/null and "b/blogs/img/javaSE_\345\212\250\346\200\201\344\273\243\347\220\206\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\233\276.jpg" differ diff --git "a/blogs/img/javaSE_\346\263\233\345\236\213\350\260\203\350\257\225\346\210\252\345\233\276-1.png" "b/blogs/img/javaSE_\346\263\233\345\236\213\350\260\203\350\257\225\346\210\252\345\233\276-1.png" new file mode 100644 index 0000000..365eb5f Binary files /dev/null and "b/blogs/img/javaSE_\346\263\233\345\236\213\350\260\203\350\257\225\346\210\252\345\233\276-1.png" differ diff --git "a/blogs/img/javaSE_\347\261\273\345\212\240\350\275\275\345\231\250\347\273\223\346\236\204\345\233\276.png" "b/blogs/img/javaSE_\347\261\273\345\212\240\350\275\275\345\231\250\347\273\223\346\236\204\345\233\276.png" new file mode 100644 index 0000000..5953249 Binary files /dev/null and "b/blogs/img/javaSE_\347\261\273\345\212\240\350\275\275\345\231\250\347\273\223\346\236\204\345\233\276.png" differ diff --git a/blogs/img/javaweb_HttpResponseStatus.png b/blogs/img/javaweb_HttpResponseStatus.png new file mode 100644 index 0000000..d9e83d2 Binary files /dev/null and b/blogs/img/javaweb_HttpResponseStatus.png differ diff --git a/blogs/img/javaweb_servlet-lifecycle.png b/blogs/img/javaweb_servlet-lifecycle.png new file mode 100644 index 0000000..ba13f14 Binary files /dev/null and b/blogs/img/javaweb_servlet-lifecycle.png differ diff --git "a/blogs/img/javaweb_servlet-url\345\214\271\351\205\215.png" "b/blogs/img/javaweb_servlet-url\345\214\271\351\205\215.png" new file mode 100644 index 0000000..c561a3b Binary files /dev/null and "b/blogs/img/javaweb_servlet-url\345\214\271\351\205\215.png" differ diff --git "a/blogs/img/javaweb_tomcat\344\275\223\347\263\273\347\273\223\346\236\204.png" "b/blogs/img/javaweb_tomcat\344\275\223\347\263\273\347\273\223\346\236\204.png" new file mode 100644 index 0000000..fd0037e Binary files /dev/null and "b/blogs/img/javaweb_tomcat\344\275\223\347\263\273\347\273\223\346\236\204.png" differ diff --git "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(1)-\345\217\215\345\260\204.md" "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(1)-\345\217\215\345\260\204.md" index 52367a4..cf7a1e4 100644 --- "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(1)-\345\217\215\345\260\204.md" +++ "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(1)-\345\217\215\345\260\204.md" @@ -7,7 +7,6 @@ **Contents** - [java基础巩固笔记(1)-反射](#java基础巩固笔记1-反射) -- [反射](#反射) - [反射基本使用](#反射基本使用) - [数组的反射](#数组的反射) - [配置文件加载](#配置文件加载) @@ -17,7 +16,6 @@ --- -# 反射 **反射:将类的属性和方法映射成相应的类。** @@ -94,10 +92,10 @@ hashcode与内存泄露问题 参考java api: >* hashcode一旦生成,不要变 ->* 对象equals方法返回true,则hascode要一致 ->* 反之,equals方法返回false,hascode不一定互异 +>* 对象equals方法返回true,则hashcode要一致 +>* 反之,equals方法返回false,hashcode不一定互异 -如果参与hascode计算的成员变量中途发生变化,则后面remove时失败,造成内存泄露 +如果参与hashcode计算的成员变量中途发生变化,则后面remove时失败,造成内存泄露 ---- @@ -134,7 +132,7 @@ hashcode与内存泄露问题 --- -## 内省(Instropector) & JavaBean +## 内省(Introspector) & JavaBean JavaBean读取属性x的值的流程:变大写、补前缀、获取方法。 ``` diff --git "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(2)-\346\263\233\345\236\213.md" "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(2)-\346\263\233\345\236\213.md" index 59aa512..4397bdc 100644 --- "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(2)-\346\263\233\345\236\213.md" +++ "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(2)-\346\263\233\345\236\213.md" @@ -7,7 +7,6 @@ **Contents** - [java基础巩固笔记(2)-泛型](#java基础巩固笔记2-泛型) -- [泛型](#泛型) - [术语](#术语) - ["?"通配符](#通配符) - [通配符的扩展](#通配符的扩展) @@ -25,8 +24,8 @@ 本文对泛型的基本知识进行较为全面的总结,并附上简短的代码实例,加深记忆。 -# 泛型 -将集合中的元素限定为一个特定的类型。 + +泛型:将集合中的元素限定为一个特定的类型。 ## 术语 @@ -178,7 +177,7 @@ Error:(17, 29) java: 不兼容的类型: 推断类型不符合上限 ``` 但是有一点没搞清楚,我在IDEA里面单步调试,发现结果如下图: -![泛型调试截图-1](http://7xph6d.com1.z0.glb.clouddn.com/javaSE_%E6%B3%9B%E5%9E%8B%E8%B0%83%E8%AF%95%E6%88%AA%E5%9B%BE-1.png) +![泛型调试截图-1](/blogs/img/javaSE_%E6%B3%9B%E5%9E%8B%E8%B0%83%E8%AF%95%E6%88%AA%E5%9B%BE-1.png) 不知道b为什么是Double类型的(但直接`Double b`接收返回值会编译报错)。不知道跟IDE有没有关系,是不是IDE在debug时会显示这个对象最精确的类型? diff --git "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(3)-\347\261\273\345\212\240\350\275\275\345\231\250.md" "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(3)-\347\261\273\345\212\240\350\275\275\345\231\250.md" index 1d244b6..e66d0ee 100644 --- "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(3)-\347\261\273\345\212\240\350\275\275\345\231\250.md" +++ "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(3)-\347\261\273\345\212\240\350\275\275\345\231\250.md" @@ -7,7 +7,6 @@ **Contents** - [java基础巩固笔记(3)-类加载器](#java基础巩固笔记3-类加载器) -- [类加载器](#类加载器) - [默认类加载器](#默认类加载器) - [类加载器的委托机制](#类加载器的委托机制) - [自定义类加载器的编写原理](#自定义类加载器的编写原理) @@ -17,19 +16,16 @@ --- -# 类加载器 java类加载器就是在运行时在JVM中动态地加载所需的类,java类加载器基于三个机制:委托,可见,单一。 -把classpath下的那些.class文件加载进内存,处理后成为字节码,这些工作是类加载器做的。 +把 classpath 下的那些 `.class` 文件加载进内存,处理后形成可以被虚拟机直接使用的 Java 类型,这些工作是类加载器做的。 - **委托机制**指的是将加载类的请求传递给父加载器,如果父加载器找不到或者不能加载这个类,那么再加载他。 - **可见性机制**指的是父加载器加载的类都能被子加载器看见,但是子加载器加载的类父加载器是看不见的。 - **单一性机制**指的是一个类只能被同一种加载器加载一次。 - - ## 默认类加载器 系统默认三个类加载器: @@ -66,14 +62,14 @@ Exception in thread "main" java.lang.NullPointerException ## 类加载器的委托机制 类加载器的树状图 -![类加载器](http://7xph6d.com1.z0.glb.clouddn.com/javaSE_%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E7%BB%93%E6%9E%84%E5%9B%BE.png) +![类加载器](/blogs/img/javaSE_%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E7%BB%93%E6%9E%84%E5%9B%BE.png) 一般加载类的顺序: - 首先当前线程的类加载器去加载线程中的第一个类 - 如果类A应用了类B,java虚拟机将使用加载类A的类加载器来加载类B -- 还可以直接调用ClassLoader.loadClass()方法来制定某个类加载器去加载某个类 +- 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类 ------------------- diff --git "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(4)-\344\273\243\347\220\206.md" "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(4)-\344\273\243\347\220\206.md" index e22fc0a..706c592 100644 --- "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(4)-\344\273\243\347\220\206.md" +++ "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(4)-\344\273\243\347\220\206.md" @@ -26,7 +26,7 @@ - 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,也就是说在程序运行前代理类的.class文件就已经存在。 - 动态代理:在程序运行时运用反射机制动态创建生成。 -![代理架构图](http://7xph6d.com1.z0.glb.clouddn.com/javaSE_%E4%BB%A3%E7%90%86%E6%9E%B6%E6%9E%84%E5%9B%BE.png) +![代理架构图](/blogs/img/javaSE_%E4%BB%A3%E7%90%86%E6%9E%B6%E6%9E%84%E5%9B%BE.png) *紫色箭头代表类的继承关系,红色连线表示调用关系* @@ -62,7 +62,7 @@ public class ProxyTest { System.out.println(clazzProxy1); printConstructors(clazzProxy1); printMethods(clazzProxy1); - + } /** @@ -239,14 +239,14 @@ com.sun.proxy.$Proxy0 ### 动态代理的工作原理 -代理类创建时需要传入一个InvocationHandler对象,client调用代理类,代理类的相应方法调用InvocationHandler的的invoke方法,InvocationHandler的的invoke方法(可在其中加入日志记录、时间统计等附加功能)再找目标类的相应方法。 +代理类创建时需要传入一个InvocationHandler对象,client调用代理类,代理类的相应方法调用InvocationHandler的的invoke方法,InvocationHandler的invoke方法(可在其中加入日志记录、时间统计等附加功能)再找目标类的相应方法。 -![动态代理的工作原理图](http://7xph6d.com1.z0.glb.clouddn.com/javaSE_%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%9B%BE.jpg) +![动态代理的工作原理图](/blogs/img/javaSE_%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%9B%BE.jpg) ### 面向切面编程 -把切面的代码以**对象**的形式传递给InvocationHandler的的invoke方法,invoke方法中执行该对象的方法就执行了切面的代码。 - +把切面的代码以**对象**的形式传递给InvocationHandler的invoke方法,invoke方法中执行该对象的方法就执行了切面的代码。 + 所以需要传递两个参数: 1.目标(Object target) diff --git "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(6)-\346\263\250\350\247\243.md" "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(6)-\346\263\250\350\247\243.md" index f7fd489..0d070dd 100644 --- "a/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(6)-\346\263\250\350\247\243.md" +++ "b/blogs/javase/java\345\237\272\347\241\200\345\267\251\345\233\272\347\254\224\350\256\260(6)-\346\263\250\350\247\243.md" @@ -67,7 +67,7 @@ Class C{ ``` RetentionPolicy.SOURCE 注解将被编译器丢弃 RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃 -RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。 +RetentionPolicy.RUNTIME VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息。 ``` - `@Target` diff --git "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(1)-Tomcat.md" "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(1)-Tomcat.md" index cf97372..68ab208 100644 --- "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(1)-Tomcat.md" +++ "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(1)-Tomcat.md" @@ -91,7 +91,7 @@ mail---------------------------Web应用所在目录 ## Tomcat体系结构 -![Tomcat体系结构](http://7xph6d.com1.z0.glb.clouddn.com/javaweb_tomcat%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png) +![Tomcat体系结构](/blogs/img/javaweb_tomcat%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png) diff --git "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(2)-http\345\205\245\351\227\250.md" "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(2)-http\345\205\245\351\227\250.md" index 86cce6a..64562c8 100644 --- "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(2)-http\345\205\245\351\227\250.md" +++ "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(2)-http\345\205\245\351\227\250.md" @@ -48,7 +48,7 @@ ### 响应状态行 -![HTTP响应状态码简表](http://7xph6d.com1.z0.glb.clouddn.com/javaweb_HttpResponseStatus.png) +![HTTP响应状态码简表](/blogs/img/javaweb_HttpResponseStatus.png) 详情可参考 diff --git "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(3)-Servlet.md" "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(3)-Servlet.md" index 06c051f..e43df2f 100644 --- "a/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(3)-Servlet.md" +++ "b/blogs/javaweb/javaweb\345\205\245\351\227\250\347\254\224\350\256\260(3)-Servlet.md" @@ -69,7 +69,7 @@ public class FirstServlet extends GenericServlet{ 时序图 -![servlet的调用过程和生命周期](http://7xph6d.com1.z0.glb.clouddn.com/javaweb_servlet-lifecycle.png) +![servlet的调用过程和生命周期](/blogs/img/javaweb_servlet-lifecycle.png) ## servlet开发的一些细节 @@ -79,7 +79,7 @@ public class FirstServlet extends GenericServlet{ - **通配符**:``的``可以使用通配符,两种固定格式:`*.扩展名`;以`/`开头,以`/*`结尾 -![javaweb_servlet-url匹配.png](http://7xph6d.com1.z0.glb.clouddn.com/javaweb_servlet-url%E5%8C%B9%E9%85%8D.png) +![javaweb_servlet-url匹配.png](/blogs/img/javaweb_servlet-url%E5%8C%B9%E9%85%8D.png) - **对象**:servlet由servlet引擎调用,不能独立运行。客户端多次请求,服务器只创建一个servlet实例,之后驻留内存中继续服务直至web容器退出才销毁它。 - **请求**:服务器针对客户端的每一次请求都会创建新的`request`和`response`对象(它们的生命周期很短),传给`service`方法。 diff --git a/java-io/pom.xml b/java-io/pom.xml index d62726d..048ab63 100644 --- a/java-io/pom.xml +++ b/java-io/pom.xml @@ -11,4 +11,10 @@ java-io + + + junit + junit + + \ No newline at end of file diff --git a/java-io/src/main/java/com/brianway/learning/java/io/DirList.java b/java-io/src/main/java/com/brianway/learning/java/io/DirList.java index c7e8898..ca883af 100644 --- a/java-io/src/main/java/com/brianway/learning/java/io/DirList.java +++ b/java-io/src/main/java/com/brianway/learning/java/io/DirList.java @@ -24,6 +24,8 @@ public boolean accept(File dir, String name) { } }); } + + if (list == null) return; Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); for (String dirItem : list) { System.out.println(dirItem); diff --git a/java-io/src/main/java/com/brianway/learning/java/io/TextFile.java b/java-io/src/main/java/com/brianway/learning/java/io/TextFile.java index 6170202..cf574ba 100755 --- a/java-io/src/main/java/com/brianway/learning/java/io/TextFile.java +++ b/java-io/src/main/java/com/brianway/learning/java/io/TextFile.java @@ -94,7 +94,7 @@ public static void main(String[] args) { TextFile text = new TextFile(parent + "/text.txt"); text.write(parent + "test2.txt"); // Break into unique sorted list of words: - TreeSet words = new TreeSet( + TreeSet words = new TreeSet<>( new TextFile(inFileName, "\\W+")); // Display the capitalized words: System.out.println(words.headSet("a")); diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/BufferToText.java b/java-io/src/main/java/com/brianway/learning/java/nio/example/BufferToText.java similarity index 97% rename from java-io/src/main/java/com/brianway/learning/java/nio/BufferToText.java rename to java-io/src/main/java/com/brianway/learning/java/nio/example/BufferToText.java index 6d9c5d7..718fe64 100755 Binary files a/java-io/src/main/java/com/brianway/learning/java/nio/BufferToText.java and b/java-io/src/main/java/com/brianway/learning/java/nio/example/BufferToText.java differ diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/ChannelCopy.java b/java-io/src/main/java/com/brianway/learning/java/nio/example/ChannelCopy.java similarity index 96% rename from java-io/src/main/java/com/brianway/learning/java/nio/ChannelCopy.java rename to java-io/src/main/java/com/brianway/learning/java/nio/example/ChannelCopy.java index 8054ebc..f439a48 100644 --- a/java-io/src/main/java/com/brianway/learning/java/nio/ChannelCopy.java +++ b/java-io/src/main/java/com/brianway/learning/java/nio/example/ChannelCopy.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java.nio; +package com.brianway.learning.java.nio.example; import java.io.FileInputStream; import java.io.FileOutputStream; diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/Endians.java b/java-io/src/main/java/com/brianway/learning/java/nio/example/Endians.java similarity index 95% rename from java-io/src/main/java/com/brianway/learning/java/nio/Endians.java rename to java-io/src/main/java/com/brianway/learning/java/nio/example/Endians.java index f7b2404..8cd74be 100644 --- a/java-io/src/main/java/com/brianway/learning/java/nio/Endians.java +++ b/java-io/src/main/java/com/brianway/learning/java/nio/example/Endians.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java.nio; +package com.brianway.learning.java.nio.example; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/GetChannel.java b/java-io/src/main/java/com/brianway/learning/java/nio/example/GetChannel.java similarity index 96% rename from java-io/src/main/java/com/brianway/learning/java/nio/GetChannel.java rename to java-io/src/main/java/com/brianway/learning/java/nio/example/GetChannel.java index 9a23798..4a0f22c 100644 --- a/java-io/src/main/java/com/brianway/learning/java/nio/GetChannel.java +++ b/java-io/src/main/java/com/brianway/learning/java/nio/example/GetChannel.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java.nio; +package com.brianway.learning.java.nio.example; import java.io.FileInputStream; import java.io.FileOutputStream; diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/ViewBuffers.java b/java-io/src/main/java/com/brianway/learning/java/nio/example/ViewBuffers.java similarity index 94% rename from java-io/src/main/java/com/brianway/learning/java/nio/ViewBuffers.java rename to java-io/src/main/java/com/brianway/learning/java/nio/example/ViewBuffers.java index f95b2a5..ba703dd 100755 --- a/java-io/src/main/java/com/brianway/learning/java/nio/ViewBuffers.java +++ b/java-io/src/main/java/com/brianway/learning/java/nio/example/ViewBuffers.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java.nio;//: io/ViewBuffers.java +package com.brianway.learning.java.nio.example;//: io/ViewBuffers.java import java.nio.ByteBuffer; import java.nio.CharBuffer; diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/BufferDemo.java b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/BufferDemo.java new file mode 100644 index 0000000..9eb1061 --- /dev/null +++ b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/BufferDemo.java @@ -0,0 +1,41 @@ +package com.brianway.learning.java.nio.tutorial; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * 使用Buffer的例子 + * + * @auther brian + * @since 2019/6/18 00:26 + */ +public class BufferDemo { + public static void main(String[] args) throws IOException { + String path = BufferDemo.class.getResource("/").getPath() + "buffer-demo.txt"; + RandomAccessFile aFile = new RandomAccessFile(path, "rw"); + FileChannel inChannel = aFile.getChannel(); + + //create buffer with capacity of 48 bytes + ByteBuffer buf = ByteBuffer.allocate(48); + + //read into buffer. + int bytesRead = inChannel.read(buf); + while (bytesRead != -1) { + + //make buffer ready for read + buf.flip(); + while (buf.hasRemaining()) { + // read 1 byte at a time + System.out.print((char) buf.get()); + } + + //make buffer ready for writing + // System.out.println("------"); + buf.clear(); + bytesRead = inChannel.read(buf); + } + aFile.close(); + } +} diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/ChannelDemo.java b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/ChannelDemo.java new file mode 100644 index 0000000..18adc75 --- /dev/null +++ b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/ChannelDemo.java @@ -0,0 +1,43 @@ +package com.brianway.learning.java.nio.tutorial; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * 基本的 Channel 示例 + * 一个使用FileChannel读取数据到Buffer中的示例 + * + * @auther brian + * @since 2019/6/17 23:41 + */ +public class ChannelDemo { + public static void main(String[] args) { + try { + String path = ChannelDemo.class.getResource("/").getPath() + "channel-demo.txt"; + RandomAccessFile aFile = new RandomAccessFile(path, "rw"); + FileChannel inChannel = aFile.getChannel(); + // 该值小于文件内容的长度时,结果不同 + int bufferSize = 48; + ByteBuffer buf = ByteBuffer.allocate(bufferSize); + + int bytesRead = inChannel.read(buf); + while (bytesRead != -1) { + + System.out.println("Read " + bytesRead); + buf.flip(); + + while (buf.hasRemaining()) { + System.out.print((char) buf.get()); + } + + buf.clear(); + bytesRead = inChannel.read(buf); + } + aFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/SelectorDemo.java b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/SelectorDemo.java new file mode 100644 index 0000000..99bf0a8 --- /dev/null +++ b/java-io/src/main/java/com/brianway/learning/java/nio/tutorial/SelectorDemo.java @@ -0,0 +1,64 @@ +package com.brianway.learning.java.nio.tutorial; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.util.Iterator; +import java.util.Set; + +/** + * Full Selector Example + * 启动后,浏览器输入 localhost:9999 + * + * @auther brian + * @since 2019/6/24 22:29 + */ +public class SelectorDemo { + + public static void main(String[] args) throws IOException { + Selector selector = Selector.open(); + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(new InetSocketAddress(9999)); + serverSocketChannel.configureBlocking(false); + + // SelectionKey key = + serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); + + while (true) { + + int readyChannels = selector.selectNow(); + + if (readyChannels == 0) { + // System.out.println("readyChannels == 0"); + continue; + } + + Set selectedKeys = selector.selectedKeys(); + + Iterator keyIterator = selectedKeys.iterator(); + + while (keyIterator.hasNext()) { + + SelectionKey key = keyIterator.next(); + + if (key.isAcceptable()) { + // a connection was accepted by a ServerSocketChannel. + System.out.println("accepted"); + } else if (key.isConnectable()) { + // a connection was established with a remote server. + System.out.println("connectable"); + } else if (key.isReadable()) { + // a channel is ready for reading + System.out.println("ready"); + } else if (key.isWritable()) { + // a channel is ready for writing + System.out.println("writable"); + } + + keyIterator.remove(); + } + } + } +} diff --git a/java-io/src/main/resources/buffer-demo.txt b/java-io/src/main/resources/buffer-demo.txt new file mode 100644 index 0000000..991243a --- /dev/null +++ b/java-io/src/main/resources/buffer-demo.txt @@ -0,0 +1,20 @@ +0123456789 +asdf +123456789 +asdf +23456789 +asdf +3456789 +asdf +456789 +asdf +0123456789 +asdf +123456789 +asdf +23456789 +asdf +3456789 +asdf +456789 +asdf diff --git a/java-io/src/main/resources/channel-demo.txt b/java-io/src/main/resources/channel-demo.txt new file mode 100644 index 0000000..a4f45f9 --- /dev/null +++ b/java-io/src/main/resources/channel-demo.txt @@ -0,0 +1,2 @@ +0123456789 +asdf diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/ChannelTransferTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/ChannelTransferTest.java new file mode 100644 index 0000000..aac8b65 --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/ChannelTransferTest.java @@ -0,0 +1,60 @@ +package com.brianway.java.nio.tutorial; + +import com.brianway.learning.java.nio.tutorial.BufferDemo; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; + +/** + * 通道之间的数据传输 + * + * @auther brian + * @since 2019/6/19 00:20 + */ +public class ChannelTransferTest { + private String fromPath = BufferDemo.class.getResource("/").getPath() + "fromFile.txt"; + private String toPath = BufferDemo.class.getResource("/").getPath() + "toFile.txt"; + + @Test + public void testTransferFrom() { + try { + RandomAccessFile fromFile = new RandomAccessFile(fromPath, "rw"); + FileChannel fromChannel = fromFile.getChannel(); + + RandomAccessFile toFile = new RandomAccessFile(toPath, "rw"); + FileChannel toChannel = toFile.getChannel(); + + long position = 0; + long count = fromChannel.size(); + + long transferBytesSize = + toChannel.transferFrom(fromChannel, position, count); + Assert.assertEquals(26, transferBytesSize); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testTransferTo() { + try { + RandomAccessFile fromFile = new RandomAccessFile(fromPath, "rw"); + FileChannel fromChannel = fromFile.getChannel(); + + RandomAccessFile toFile = new RandomAccessFile(toPath, "rw"); + FileChannel toChannel = toFile.getChannel(); + + long position = 0; + long count = fromChannel.size(); + + long transferBytesSize = + fromChannel.transferTo(position, count, toChannel); + Assert.assertEquals(26, transferBytesSize); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/FileChannelTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/FileChannelTest.java new file mode 100644 index 0000000..ca17f16 --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/FileChannelTest.java @@ -0,0 +1,31 @@ +package com.brianway.java.nio.tutorial; + +import com.brianway.learning.java.nio.tutorial.BufferDemo; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; + +/** + * FileChannel测试 + * + * @auther brian + * @since 2019/6/24 23:00 + */ +public class FileChannelTest { + private String dir = BufferDemo.class.getResource("/").getPath(); + private String path = BufferDemo.class.getResource("/").getPath() + "fileChannel.txt"; + + @Test + public void testTruncate() throws IOException { + RandomAccessFile aFile = new RandomAccessFile(dir + "truncate.txt", "rw"); + FileChannel channel = aFile.getChannel(); + int size = 10; + // Truncates this channel's file to the given size. + channel.truncate(size); + long fileSize = channel.size(); + Assert.assertEquals(size, fileSize); + } +} diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/GatherTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/GatherTest.java new file mode 100644 index 0000000..5e04221 --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/GatherTest.java @@ -0,0 +1,37 @@ +package com.brianway.java.nio.tutorial; + +import com.brianway.learning.java.nio.tutorial.BufferDemo; +import org.junit.Test; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; + +/** + * @auther brian + * @since 2019/6/18 23:51 + */ +public class GatherTest { + + private String path = BufferDemo.class.getResource("/").getPath() + "gather.txt"; + + @Test + public void testGatheringWrites() throws IOException { + RandomAccessFile aFile = new RandomAccessFile(path, "rw"); + FileChannel channel = aFile.getChannel(); + ByteBuffer header = ByteBuffer.allocate(128); + ByteBuffer body = ByteBuffer.allocate(1024); + + header.put("这是头".getBytes(Charset.forName("UTF-8"))); + body.put("this is body.".getBytes(Charset.forName("UTF-8"))); + header.flip(); + body.flip(); + //write data into buffers + ByteBuffer[] bufferArray = {header, body}; + // 注意只有position和limit之间的数据才会被写入 + channel.write(bufferArray); + channel.close(); + } +} diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/ScatterTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/ScatterTest.java new file mode 100644 index 0000000..a02ddbf --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/ScatterTest.java @@ -0,0 +1,53 @@ +package com.brianway.java.nio.tutorial; + +import com.brianway.learning.java.nio.tutorial.BufferDemo; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * @auther brian + * @since 2019/6/18 23:24 + */ +public class ScatterTest { + + private String path = BufferDemo.class.getResource("/").getPath() + "scatter.txt"; + + @Test + public void testScatteringReads() throws IOException { + RandomAccessFile aFile = new RandomAccessFile(path, "rw"); + FileChannel fc = aFile.getChannel(); + + //create buffer with capacity of 48 bytes + ByteBuffer header = ByteBuffer.allocate(8); + ByteBuffer body = ByteBuffer.allocate(1024); + + ByteBuffer[] bufferArray = {header, body}; + long bytesRead = fc.read(bufferArray); + // System.out.println(bytesRead); + Assert.assertEquals(26, bytesRead); + //print header + System.out.println("---header(" + header.limit() + "bytes)---"); + header.flip(); + while (header.hasRemaining()) { + // read 1 byte at a time + System.out.print((char) header.get()); + } + header.clear(); + + // print body + body.flip(); + System.out.println("---body(" + body.limit() + "bytes)----"); + while (body.hasRemaining()) { + // read 1 byte at a time + System.out.print((char) body.get()); + } + header.clear(); + body.clear(); + fc.close(); + } +} diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/ServerSocketChannelTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/ServerSocketChannelTest.java new file mode 100644 index 0000000..30ed0f1 --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/ServerSocketChannelTest.java @@ -0,0 +1,48 @@ +package com.brianway.java.nio.tutorial; + +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +/** + * @auther brian + * @since 2019/6/25 00:31 + */ +public class ServerSocketChannelTest { + + @Test + public void testOpen() throws IOException { + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(new InetSocketAddress(9999)); + while (true) { + System.out.println("waiting..."); + SocketChannel socketChannel = + serverSocketChannel.accept(); + //do something with socketChannel... + System.out.println("connected: " + socketChannel.toString()); + } + } + + @Test + public void testNonBlocking() throws IOException { + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + + serverSocketChannel.socket().bind(new InetSocketAddress(9999)); + serverSocketChannel.configureBlocking(false); + + while (true) { + System.out.println("waiting..."); + SocketChannel socketChannel = + serverSocketChannel.accept(); + + if (socketChannel != null) { + //do something with socketChannel... + System.out.println("connected: " + socketChannel.toString()); + } + } + } + +} diff --git a/java-io/src/test/java/com/brianway/java/nio/tutorial/SocketChannelTest.java b/java-io/src/test/java/com/brianway/java/nio/tutorial/SocketChannelTest.java new file mode 100644 index 0000000..3146572 --- /dev/null +++ b/java-io/src/test/java/com/brianway/java/nio/tutorial/SocketChannelTest.java @@ -0,0 +1,37 @@ +package com.brianway.java.nio.tutorial; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; + +/** + * @auther brian + * @since 2019/6/24 23:33 + */ +public class SocketChannelTest { + + @Test + public void testConnect() throws IOException { + SocketChannel socketChannel = SocketChannel.open(); + boolean connected = socketChannel.connect(new InetSocketAddress("www.baidu.com", 80)); + Assert.assertEquals(true, connected); + socketChannel.close(); + } + + @Test + public void testNonBlocking() throws IOException { + SocketChannel socketChannel = SocketChannel.open(); + socketChannel.configureBlocking(false); + boolean connected = socketChannel.connect(new InetSocketAddress("www.baidu.com", 80)); + System.out.println("connected: " + connected); + while (!socketChannel.finishConnect()) { + //wait, or do something else... + System.out.println("not finish"); + } + System.out.println("finished"); + } + +} diff --git a/java-io/src/test/resources/fileChannel.txt b/java-io/src/test/resources/fileChannel.txt new file mode 100644 index 0000000..32ca54f --- /dev/null +++ b/java-io/src/test/resources/fileChannel.txt @@ -0,0 +1,4 @@ +asdasdas +ahfsadkjhsdfkjl +23232323 +2323 \ No newline at end of file diff --git a/java-io/src/test/resources/fromFile.txt b/java-io/src/test/resources/fromFile.txt new file mode 100644 index 0000000..dbd2aa4 --- /dev/null +++ b/java-io/src/test/resources/fromFile.txt @@ -0,0 +1,3 @@ +1234567 +qwertyusfdf +asdsad \ No newline at end of file diff --git a/java-io/src/test/resources/scatter.txt b/java-io/src/test/resources/scatter.txt new file mode 100644 index 0000000..dbd2aa4 --- /dev/null +++ b/java-io/src/test/resources/scatter.txt @@ -0,0 +1,3 @@ +1234567 +qwertyusfdf +asdsad \ No newline at end of file diff --git a/java-io/src/test/resources/truncate.txt b/java-io/src/test/resources/truncate.txt new file mode 100644 index 0000000..32ca54f --- /dev/null +++ b/java-io/src/test/resources/truncate.txt @@ -0,0 +1,4 @@ +asdasdas +ahfsadkjhsdfkjl +23232323 +2323 \ No newline at end of file diff --git a/java8/README.md b/java8/README.md index 4827316..542462c 100644 --- a/java8/README.md +++ b/java8/README.md @@ -45,3 +45,42 @@ - ParallelStreams: 并行流计算 1~n 的和,分别使用指令式,串行迭代流,并行迭代流,基本类型流,有副作用的流 - ForkJoinSumCalculator: 使用分支/合并框架执行并行求和 - WordCount: Spliterator: splitable iterator + +## 高效 Java 8 编程 + +- StrategyMain: 策略模式 +- TemplateMain: 模版方法 +- ObserverMain: 观察者模式 +- ChainOfResponsibilityMain: 责任链模式 +- FactoryMain: 工厂模式 +- Debugging: 查看栈跟踪 +- Peek: 使用日志调试 +- Ambiguous: 默认方法解决冲突 + + +Optional + +- Car,Insurance,Person: 模型类 +- OptionalMain: ptional 用法(map,flatMap,filter) +- OptionalInAction: 使用 Optional 实战示例 +- ReadPositiveIntParam: 使用 Optional 示例 + +CompletableFuture + +- v1/Shop: 商店类第一版,没有折扣 +- v1/ShopMain: 使用异步 API 的使用示例 +- v1/BestPriceFinder: 通过不同的方案实现价格查询:顺序流,并行流,CompletableFuture +- v1/BestPriceFinderMain: 测试每种实现方案的执行时间 + +- AsyncShop: 使用工厂方法 supplyAsync 创建 CompletableFuture +- AsyncShopClient: 演示错误处理 +- ExchangeService: 货币兑换类 +- Discount: 折扣类 +- Quote: 解析商店返回的字符串 +- Shop: 商店类,报价按照 `name:price:code` 的格式 +- BestPriceFinder: 添加折扣后的版本 +- BestPriceFinderMain: 测试每种实现方案的执行时间 + +时间和日期 + +- DateTimeExamples: 新的时间和日期 API 示例 \ No newline at end of file diff --git a/java8/pom.xml b/java8/pom.xml index 9428faf..6868bb2 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -11,4 +11,11 @@ java8 + + + junit + junit + + + \ No newline at end of file diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/Ambiguous.java b/java8/src/main/java/com/brianway/learning/java8/effective/Ambiguous.java new file mode 100644 index 0000000..5c43b5d --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/Ambiguous.java @@ -0,0 +1,30 @@ +package com.brianway.learning.java8.effective; + +/** + * 默认方法解决冲突 + */ +public class Ambiguous { + + public static void main(String... args) { + new C().hello(); + } + + interface A { + default void hello() { + System.out.println("Hello from A"); + } + } + + interface B { + default void hello() { + System.out.println("Hello from B"); + } + } + + static class C implements B, A { + public void hello() { + A.super.hello();// Java 8 引入的新语法: X.super.m(...) + // 其中 X 为希望调用 m 方法所在的父接口 + } + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShop.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShop.java new file mode 100644 index 0000000..d33bcef --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShop.java @@ -0,0 +1,48 @@ +package com.brianway.learning.java8.effective.async; + +import static com.brianway.learning.java8.effective.async.Util.delay; +import static com.brianway.learning.java8.effective.async.Util.format; + +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +/** + * 使用工厂方法 supplyAsync 创建 CompletableFuture + */ +public class AsyncShop { + + private final String name; + private final Random random; + + public AsyncShop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public Future getPrice(String product) { +/* // 抛出 CompletableFuture 内的异常 + CompletableFuture futurePrice = new CompletableFuture<>(); + new Thread( () -> { + try { + double price = calculatePrice(product); + futurePrice.complete(price); + } catch (Exception ex) { + futurePrice.completeExceptionally(ex); + } + }).start(); + return futurePrice; +*/ + // 使用工厂方法 + return CompletableFuture.supplyAsync(() -> calculatePrice(product)); + } + + private double calculatePrice(String product) { + delay(); + if (true) { + throw new RuntimeException("product not available"); + } + return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); + } + +} \ No newline at end of file diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShopClient.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShopClient.java new file mode 100644 index 0000000..e9c6ce5 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/AsyncShopClient.java @@ -0,0 +1,24 @@ +package com.brianway.learning.java8.effective.async; + +import java.util.concurrent.Future; + +/** + * 演示错误处理 + */ +public class AsyncShopClient { + + public static void main(String[] args) { + AsyncShop shop = new AsyncShop("BestShop"); + long start = System.nanoTime(); + Future futurePrice = shop.getPrice("myPhone"); + long incocationTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Invocation returned after " + incocationTime + " msecs"); + try { + System.out.println("Price is " + futurePrice.get()); + } catch (Exception e) { + throw new RuntimeException(e); + } + long retrivalTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Price returned after " + retrivalTime + " msecs"); + } +} \ No newline at end of file diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinder.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinder.java new file mode 100644 index 0000000..808a13c --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinder.java @@ -0,0 +1,85 @@ +package com.brianway.learning.java8.effective.async; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 添加折扣后的版本 + * 构造同步和异步操作 + */ +public class BestPriceFinder { + + private final List shops = Arrays.asList(new Shop("BestPrice"), + new Shop("LetsSaveBig"), + new Shop("MyFavoriteShop"), + new Shop("BuyItAll"), + new Shop("ShopEasy")); + + private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } + }); + + public List findPricesSequential(String product) { + return shops.stream() + .map(shop -> shop.getPrice(product)) + .map(Quote::parse) + .map(Discount::applyDiscount) + .collect(Collectors.toList()); + } + + public List findPricesParallel(String product) { + return shops.parallelStream() + .map(shop -> shop.getPrice(product)) + .map(Quote::parse) + .map(Discount::applyDiscount) + .collect(Collectors.toList()); + } + + public List findPricesFuture(String product) { + List> priceFutures = findPricesStream(product) + .collect(Collectors.toList()); + + return priceFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + } + + /** + * 分了三步: + * 1. 获取价格 + * 2. 解析报价 + * 3. 为计算折扣价格构造 Future + */ + public Stream> findPricesStream(String product) { + return shops.stream() + .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)) + .map(future -> future.thenApply(Quote::parse)) + .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync( + () -> Discount.applyDiscount(quote), executor))); + } + + /** + * 响应 CompletableFuture 的 completion 事件 + * 把 Util 类中的 delay 调一下效果更明显 + */ + public void printPricesStream(String product) { + long start = System.nanoTime(); + CompletableFuture[] futures = findPricesStream(product) + .map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)"))) + .toArray(CompletableFuture[]::new); + CompletableFuture.allOf(futures).join(); + System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs"); + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinderMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinderMain.java new file mode 100644 index 0000000..a0994e0 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/BestPriceFinderMain.java @@ -0,0 +1,27 @@ +package com.brianway.learning.java8.effective.async; + +import java.util.List; +import java.util.function.Supplier; + +/** + * 测试每种实现方案的执行时间 + */ +public class BestPriceFinderMain { + + private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); + + public static void main(String[] args) { + execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); + execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); + execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); + bestPriceFinder.printPricesStream("myPhone27S"); + } + + private static void execute(String msg, Supplier> s) { + long start = System.nanoTime(); + System.out.println(s.get()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.println(msg + " done in " + duration + " msecs"); + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/Discount.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/Discount.java new file mode 100644 index 0000000..992012c --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/Discount.java @@ -0,0 +1,33 @@ +package com.brianway.learning.java8.effective.async; + +import static com.brianway.learning.java8.effective.async.Util.delay; +import static com.brianway.learning.java8.effective.async.Util.format; + +/** + * 折扣类 + */ +public class Discount { + + public enum Code { + NONE(0), + SILVER(5), + GOLD(10), + PLATINUM(15), + DIAMOND(20); + + private final int percentage; + + Code(int percentage) { + this.percentage = percentage; + } + } + + public static String applyDiscount(Quote quote) { + return quote.getShopName() + " price is " + Discount.apply(quote.getPrice(), quote.getDiscountCode()); + } + + private static double apply(double price, Code code) { + delay(); + return format(price * (100 - code.percentage) / 100); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/ExchangeService.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/ExchangeService.java new file mode 100644 index 0000000..5b3ef55 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/ExchangeService.java @@ -0,0 +1,33 @@ +package com.brianway.learning.java8.effective.async; + +import static com.brianway.learning.java8.effective.async.Util.delay; + +/** + * 货币兑换类 + */ +public class ExchangeService { + + public enum Money { + USD(1.0), + EUR(1.35387), + GBP(1.69715), + CAD(.92106), + MXN(.07683); + + private final double rate; + + Money(double rate) { + this.rate = rate; + } + } + + public static double getRate(Money source, Money destination) { + return getRateWithDelay(source, destination); + } + + private static double getRateWithDelay(Money source, Money destination) { + delay(); + return destination.rate / source.rate; + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/Quote.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/Quote.java new file mode 100644 index 0000000..0550251 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/Quote.java @@ -0,0 +1,37 @@ +package com.brianway.learning.java8.effective.async; + +/** + * 解析商店返回的字符串 + */ +public class Quote { + + private final String shopName; + private final double price; + private final Discount.Code discountCode; + + public Quote(String shopName, double price, Discount.Code discountCode) { + this.shopName = shopName; + this.price = price; + this.discountCode = discountCode; + } + + public static Quote parse(String s) { + String[] split = s.split(":"); + String shopName = split[0]; + double price = Double.parseDouble(split[1]); + Discount.Code discountCode = Discount.Code.valueOf(split[2]); + return new Quote(shopName, price, discountCode); + } + + public String getShopName() { + return shopName; + } + + public double getPrice() { + return price; + } + + public Discount.Code getDiscountCode() { + return discountCode; + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/Shop.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/Shop.java new file mode 100644 index 0000000..788226d --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/Shop.java @@ -0,0 +1,36 @@ +package com.brianway.learning.java8.effective.async; + +import static com.brianway.learning.java8.effective.async.Util.delay; +import static com.brianway.learning.java8.effective.async.Util.format; + +import java.util.Random; + +/** + * 商店类 + * 报价按照 `name:price:code` 的格式 + */ +public class Shop { + + private final String name; + private final Random random; + + public Shop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public String getPrice(String product) { + double price = calculatePrice(product); + Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)]; + return name + ":" + price + ":" + code; + } + + public double calculatePrice(String product) { + delay(); + return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); + } + + public String getName() { + return name; + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/Util.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/Util.java new file mode 100644 index 0000000..fcd6db2 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/Util.java @@ -0,0 +1,46 @@ +package com.brianway.learning.java8.effective.async; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class Util { + + private static final Random RANDOM = new Random(0); + private static final DecimalFormat formatter = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US)); + + public static void delay() { + int delay = 1000; + //int delay = 500 + RANDOM.nextInt(2000); + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static double format(double number) { + synchronized (formatter) { + return new Double(formatter.format(number)); + } + } + + public static CompletableFuture> sequence(List> futures) { +/* + CompletableFuture allDoneFuture = + CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); + return allDoneFuture.thenApply(v -> + futures.stream(). + map(future -> future.join()). + collect(Collectors.toList()) + ); +*/ + return CompletableFuture.supplyAsync(() -> futures.stream(). + map(CompletableFuture::join). + collect(Collectors.toList())); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinder.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinder.java new file mode 100644 index 0000000..092c439 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinder.java @@ -0,0 +1,166 @@ +package com.brianway.learning.java8.effective.async.v1; + +import com.brianway.learning.java8.effective.async.ExchangeService; +import com.brianway.learning.java8.effective.async.ExchangeService.Money; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 通过不同的方案实现价格查询:顺序流,并行流,CompletableFuture + */ +public class BestPriceFinder { + + private final List shops = Arrays.asList(new Shop("BestPrice"), + new Shop("LetsSaveBig"), + new Shop("MyFavoriteShop"), + new Shop("BuyItAll") + //,new Shop("ShopEasy") + ); + + private final Executor executor = Executors.newFixedThreadPool( + shops.size(), + r -> { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + + }); + + public List findPricesSequential(String product) { + return shops.stream() + .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) + .collect(Collectors.toList()); + } + + public List findPricesParallel(String product) { + return shops.parallelStream() + .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) + .collect(Collectors.toList()); + } + + public List findPricesFuture(String product) { + List> priceFutures = + shops.stream() + .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + + shop.getPrice(product), executor)) + .collect(Collectors.toList()); + + List prices = priceFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + + //-------------------涉及汇率转换-------------------- + public List findPricesInUSDJava7(String product) { + ExecutorService executor = Executors.newCachedThreadPool(); + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + final Future futureRate = executor.submit(new Callable() { + public Double call() { + return ExchangeService.getRate(Money.EUR, ExchangeService.Money.USD); + } + }); + Future futurePriceInUSD = executor.submit(new Callable() { + public Double call() { + try { + double priceInEUR = shop.getPrice(product); + return priceInEUR * futureRate.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + }); + priceFutures.add(futurePriceInUSD); + } + List prices = new ArrayList<>(); + for (Future priceFuture : priceFutures) { + try { + prices.add(/*shop.getName() +*/ " price is " + priceFuture.get()); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + } + return prices; + } + + public List findPricesInUSD(String product) { + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + // Start of Listing 10.20. + // Only the type of futurePriceInUSD has been changed to + // CompletableFuture so that it is compatible with the + // CompletableFuture::join operation below. + CompletableFuture futurePriceInUSD = + CompletableFuture.supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync( + () -> ExchangeService.getRate(ExchangeService.Money.EUR, Money.USD)), + (price, rate) -> price * rate + ); + priceFutures.add(futurePriceInUSD); + } + // Drawback: The shop is not accessible anymore outside the loop, + // so the getName() call below has been commented out. + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .map(price -> /*shop.getName() +*/ " price is " + price) + .collect(Collectors.toList()); + return prices; + } + + public List findPricesInUSD2(String product) { + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + // Here, an extra operation has been added so that the shop name + // is retrieved within the loop. As a result, we now deal with + // CompletableFuture instances. + CompletableFuture futurePriceInUSD = + CompletableFuture.supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync( + () -> ExchangeService.getRate(Money.EUR, Money.USD)), + (price, rate) -> price * rate + ).thenApply(price -> shop.getName() + " price is " + price); + priceFutures.add(futurePriceInUSD); + } + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + + public List findPricesInUSD3(String product) { + // Here, the for loop has been replaced by a mapping function... + Stream> priceFuturesStream = shops + .stream() + .map(shop -> CompletableFuture + .supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync(() -> ExchangeService.getRate(Money.EUR, Money.USD)), + (price, rate) -> price * rate) + .thenApply(price -> shop.getName() + " price is " + price)); + // However, we should gather the CompletableFutures into a List so that the asynchronous + // operations are triggered before being "joined." + List> priceFutures = priceFuturesStream.collect(Collectors.toList()); + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinderMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinderMain.java new file mode 100644 index 0000000..2c463b0 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/BestPriceFinderMain.java @@ -0,0 +1,28 @@ +package com.brianway.learning.java8.effective.async.v1; + +import java.util.List; +import java.util.function.Supplier; + +/** + * 测试每种实现方案的执行时间 + */ +public class BestPriceFinderMain { + + private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); + + public static void main(String[] args) { + execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); + execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); + execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); + execute("combined USD CompletableFuture", () -> bestPriceFinder.findPricesInUSD("myPhone27S")); + execute("combined USD CompletableFuture v2", () -> bestPriceFinder.findPricesInUSD2("myPhone27S")); + execute("combined USD CompletableFuture v3", () -> bestPriceFinder.findPricesInUSD3("myPhone27S")); + } + + private static void execute(String msg, Supplier> s) { + long start = System.nanoTime(); + System.out.println(s.get()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.println(msg + " done in " + duration + " msecs"); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/Shop.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/Shop.java new file mode 100644 index 0000000..5710f4d --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/Shop.java @@ -0,0 +1,47 @@ +package com.brianway.learning.java8.effective.async.v1; + +import static com.brianway.learning.java8.effective.async.Util.delay; + +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +/** + * 商店类,第一版 + */ +public class Shop { + + private final String name; + private final Random random; + + public Shop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public double getPrice(String product) { + return calculatePrice(product); + } + + /** + * 根据商品名随机生成价格 + */ + private double calculatePrice(String product) { + delay(); + return random.nextDouble() * product.charAt(0) + product.charAt(1); + } + + public Future getPriceAsync(String product) { + CompletableFuture futurePrice = new CompletableFuture<>(); + new Thread(() -> { + double price = calculatePrice(product); + futurePrice.complete(price); + }).start(); + return futurePrice; + } + + public String getName() { + return name; + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/ShopMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/ShopMain.java new file mode 100644 index 0000000..19e4764 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/async/v1/ShopMain.java @@ -0,0 +1,35 @@ +package com.brianway.learning.java8.effective.async.v1; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * 使用异步 API + */ +public class ShopMain { + + public static void main(String[] args) { + Shop shop = new Shop("BestShop"); + long start = System.nanoTime(); + Future futurePrice = shop.getPriceAsync("my favorite product"); + long invocationTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Invocation returned after " + invocationTime + + " msecs"); + // Do some more tasks, like querying other shops + doSomethingElse(); + // while the price of the product is being calculated + try { + double price = futurePrice.get(); + System.out.printf("Price is %.2f%n", price); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + long retrievalTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Price returned after " + retrievalTime + " msecs"); + } + + private static void doSomethingElse() { + System.out.println("Doing something else..."); + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/Car.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Car.java new file mode 100644 index 0000000..82384f4 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Car.java @@ -0,0 +1,12 @@ +package com.brianway.learning.java8.effective.optional; + +import java.util.Optional; + +public class Car { + + private Optional insurance; + + public Optional getInsurance() { + return insurance; + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/Insurance.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Insurance.java new file mode 100644 index 0000000..11e4acb --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Insurance.java @@ -0,0 +1,10 @@ +package com.brianway.learning.java8.effective.optional; + +public class Insurance { + + private String name; + + public String getName() { + return name; + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalInAction.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalInAction.java new file mode 100644 index 0000000..55e0d83 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalInAction.java @@ -0,0 +1,43 @@ +package com.brianway.learning.java8.effective.optional; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Created by brian on 17/3/27. + * 使用 Optional 实战示例 + */ +public class OptionalInAction { + + private static Map map = new HashMap<>(); + + public static void main(String[] args) { + Optional value = getValue("key"); + System.out.println(value.orElse("this is null")); + + System.out.println(stringToInt("123")); + System.out.println(stringToInt("dd")); + + } + + /** + * 用 Optional 封装可能为 null 的值 + */ + public static Optional getValue(String key) { + Optional value = Optional.ofNullable(map.get(key)); + return value; + } + + /** + * 异常与 Optional 的对比 + */ + public static Optional stringToInt(String s) { + try { + return Optional.of(Integer.parseInt(s)); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalMain.java new file mode 100644 index 0000000..11546d1 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/OptionalMain.java @@ -0,0 +1,45 @@ +package com.brianway.learning.java8.effective.optional; + +import java.util.Optional; + +/** + * Optional 用法: + * - 使用 map 从 Optional 对象提取和转换值 + * - 使用 flatMap 链接 Optional 对象 + * - 两个 Optional 对象的组合 + * - 使用 filter 剔除特定值 + */ +public class OptionalMain { + + public static void main(String[] args) { + Optional person = Optional.of(new Person()); + //Optional person2 = Optional.empty(); + String name = getCarInsuranceName(person); + System.out.println(name); + } + + public static String getCarInsuranceName(Optional person) { + return person.flatMap(Person::getCar) + .flatMap(Car::getInsurance) + .map(Insurance::getName) + .orElse("Unknown"); + } + + public Insurance findCheapestInsurance(Person person, Car car) { + //省略业务逻辑 + Insurance cheapestCompany = new Insurance(); + return cheapestCompany; + } + + public Optional nullSafeFindCheapestInsurance(Optional person, Optional car) { + return person.flatMap( + p -> car.map( + c -> findCheapestInsurance(p, c))); + } + + public void PrintIfWithName(String name) { + Optional optInsurance = Optional.of(new Insurance()); + optInsurance.filter(insurance -> name.equals(insurance.getName())) + .ifPresent(x -> System.out.println("ok")); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/Person.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Person.java new file mode 100644 index 0000000..8ffaef8 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/Person.java @@ -0,0 +1,12 @@ +package com.brianway.learning.java8.effective.optional; + +import java.util.Optional; + +public class Person { + + private Car car; + + public Optional getCar() { + return Optional.ofNullable(car); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParam.java b/java8/src/main/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParam.java new file mode 100644 index 0000000..b6fa89a --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParam.java @@ -0,0 +1,44 @@ +package com.brianway.learning.java8.effective.optional; + +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static java.util.Optional.ofNullable; + +import java.util.Optional; +import java.util.Properties; + +/** + * 使用 Optional 示例 + * test case 见 test 目录 + */ +public class ReadPositiveIntParam { + + public static int readDurationImperative(Properties props, String name) { + String value = props.getProperty(name); + if (value != null) { + try { + int i = Integer.parseInt(value); + if (i > 0) { + return i; + } + } catch (NumberFormatException nfe) { + } + } + return 0; + } + + public static int readDurationWithOptional(Properties props, String name) { + return ofNullable(props.getProperty(name)) + .flatMap(ReadPositiveIntParam::s2i) + .filter(i -> i > 0).orElse(0); + } + + public static Optional s2i(String s) { + try { + return of(Integer.parseInt(s)); + } catch (NumberFormatException e) { + return empty(); + } + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/ChainOfResponsibilityMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/ChainOfResponsibilityMain.java new file mode 100644 index 0000000..57232f1 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/ChainOfResponsibilityMain.java @@ -0,0 +1,62 @@ +package com.brianway.learning.java8.effective.tech; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * 责任链模式 + */ +public class ChainOfResponsibilityMain { + + public static void main(String[] args) { + //old school + ProcessingObject p1 = new HeaderTextProcessing(); + ProcessingObject p2 = new SpellCheckerProcessing(); + p1.setSuccessor(p2); + String result1 = p1.handle("Aren't labdas really sexy?!!"); + System.out.println(result1); + + // with lambdas + UnaryOperator headerProcessing = + (String text) -> "From Raoul, Mario and Alan: " + text; + UnaryOperator spellCheckerProcessing = + (String text) -> text.replaceAll("labda", "lambda"); + Function pipeline = headerProcessing.andThen(spellCheckerProcessing); + String result2 = pipeline.apply("Aren't labdas really sexy?!!"); + System.out.println(result2); + } + + static private abstract class ProcessingObject { + protected ProcessingObject successor; + + public void setSuccessor(ProcessingObject successor) { + this.successor = successor; + } + + public T handle(T input) { + T r = handleWork(input); + if (successor != null) { + return successor.handle(r); + } + return r; + } + + abstract protected T handleWork(T input); + } + + static private class HeaderTextProcessing + extends ProcessingObject { + public String handleWork(String text) { + return "From Raoul, Mario and Alan: " + text; + } + } + + static private class SpellCheckerProcessing + extends ProcessingObject { + public String handleWork(String text) { + return text.replaceAll("labda", "lambda"); + } + } +} + + diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/Debugging.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/Debugging.java new file mode 100644 index 0000000..31b9507 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/Debugging.java @@ -0,0 +1,41 @@ +package com.brianway.learning.java8.effective.tech; + +import java.util.Arrays; +import java.util.List; + +/** + * 查看栈跟踪 + */ +public class Debugging { + public static void main(String[] args) { +// Arrays.asList(1, 2, 3).stream() +// .map(Debugging::divideByZero) +// .forEach(System.out::println); + + List points = Arrays.asList(new Point(12, 2), null); + //points.stream().map(p -> p.getX()).forEach(System.out::println); + points.stream().map(Point::getX).forEach(System.out::println); + } + + private static class Point { + private int x; + private int y; + + private Point(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + } + + public static int divideByZero(int n) { + return n / 0; + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/FactoryMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/FactoryMain.java new file mode 100644 index 0000000..f234f17 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/FactoryMain.java @@ -0,0 +1,68 @@ +package com.brianway.learning.java8.effective.tech; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 工厂模式 + */ +public class FactoryMain { + + public static void main(String[] args) { + Product p1 = ProductFactory.createProduct("loan"); + p1.printClassName(); + + // with lambdas + Supplier loanSupplier = Loan::new; + Product p2 = loanSupplier.get(); + p2.printClassName(); + + Product p3 = ProductFactory.createProductLambda("loan"); + p3.printClassName(); + } + + static private class ProductFactory { + public static Product createProduct(String name) { + switch (name) { + case "loan": + return new Loan(); + case "stock": + return new Stock(); + case "bond": + return new Bond(); + default: + throw new RuntimeException("No such product " + name); + } + } + + public static Product createProductLambda(String name) { + Supplier p = map.get(name); + if (p != null) return p.get(); + throw new RuntimeException("No such product " + name); + } + } + + private interface Product { + default void printClassName() { + System.out.println(getClass().getSimpleName()); + } + } + + static private class Loan implements Product { + } + + static private class Stock implements Product { + } + + static private class Bond implements Product { + } + + final static private Map> map = new HashMap<>(); + + static { + map.put("loan", Loan::new); + map.put("stock", Stock::new); + map.put("bond", Bond::new); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/ObserverMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/ObserverMain.java new file mode 100644 index 0000000..7f1c785 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/ObserverMain.java @@ -0,0 +1,86 @@ +package com.brianway.learning.java8.effective.tech; + +import java.util.ArrayList; +import java.util.List; + +/** + * 观察者模式 + */ +public class ObserverMain { + + public static void main(String[] args) { + //old school + Subject f = new Feed(); + f.registerObserver(new NYTimes()); + f.registerObserver(new Guardian()); + f.registerObserver(new LeMonde()); + f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); + + // with lambdas + Feed feedLambda = new Feed(); + + feedLambda.registerObserver((String tweet) -> { + if (tweet != null && tweet.contains("money")) { + System.out.println("Breaking news in NY! " + tweet); + } + }); + feedLambda.registerObserver((String tweet) -> { + if (tweet != null && tweet.contains("queen")) { + System.out.println("Yet another news in London... " + tweet); + } + }); + + feedLambda.notifyObservers("Money money money, give me money!"); + + } + + interface Observer { + void inform(String tweet); + } + + interface Subject { + void registerObserver(Observer o); + + void notifyObservers(String tweet); + } + + static private class NYTimes implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("money")) { + System.out.println("Breaking news in NY!" + tweet); + } + } + } + + static private class Guardian implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("queen")) { + System.out.println("Yet another news in London... " + tweet); + } + } + } + + static private class LeMonde implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("wine")) { + System.out.println("Today cheese, wine and news! " + tweet); + } + } + } + + static private class Feed implements Subject { + private final List observers = new ArrayList<>(); + + public void registerObserver(Observer o) { + this.observers.add(o); + } + + public void notifyObservers(String tweet) { + observers.forEach(o -> o.inform(tweet)); + } + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/Peek.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/Peek.java new file mode 100644 index 0000000..2ccf379 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/Peek.java @@ -0,0 +1,25 @@ +package com.brianway.learning.java8.effective.tech; + +import static java.util.stream.Collectors.toList; + +import java.util.List; +import java.util.stream.Stream; + +/** + * 使用日志调试 + */ +public class Peek { + + public static void main(String[] args) { + + List result = Stream.of(2, 3, 4, 5) + .peek(x -> System.out.println("taking from stream: " + x)) + .map(x -> x + 17) + .peek(x -> System.out.println("after map: " + x)) + .filter(x -> x % 2 == 0) + .peek(x -> System.out.println("after filter: " + x)) + .limit(3) + .peek(x -> System.out.println("after limit: " + x)) + .collect(toList()); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/StrategyMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/StrategyMain.java new file mode 100644 index 0000000..1cf6e5d --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/StrategyMain.java @@ -0,0 +1,49 @@ +package com.brianway.learning.java8.effective.tech; + +/** + * 策略模式 + */ +public class StrategyMain { + + public static void main(String[] args) { + // old school + Validator v1 = new Validator(new IsNumeric()); + System.out.println(v1.validate("aaaa")); + Validator v2 = new Validator(new IsAllLowerCase()); + System.out.println(v2.validate("bbbb")); + + // with lambdas + Validator v3 = new Validator((String s) -> s.matches("\\d+")); + System.out.println(v3.validate("aaaa")); + Validator v4 = new Validator((String s) -> s.matches("[a-z]+")); + System.out.println(v4.validate("bbbb")); + } + + interface ValidationStrategy { + boolean execute(String s); + } + + static private class IsAllLowerCase implements ValidationStrategy { + public boolean execute(String s) { + return s.matches("[a-z]+"); + } + } + + static private class IsNumeric implements ValidationStrategy { + public boolean execute(String s) { + return s.matches("\\d+"); + } + } + + static private class Validator { + private final ValidationStrategy strategy; + + public Validator(ValidationStrategy v) { + this.strategy = v; + } + + public boolean validate(String s) { + return strategy.execute(s); + } + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/tech/TemplateMain.java b/java8/src/main/java/com/brianway/learning/java8/effective/tech/TemplateMain.java new file mode 100644 index 0000000..b7e67f9 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/tech/TemplateMain.java @@ -0,0 +1,60 @@ +package com.brianway.learning.java8.effective.tech; + +import java.util.function.Consumer; + +/** + * Created by brian on 17/3/24. + * 模版方法 + */ +public class TemplateMain { + + public static void main(String[] args) { + // old school + new OnlineBanking() { + @Override + void makeCustomerHappy(Customer c) { + System.out.println("Hello " + c.getName()); + } + }.processCustomer(1); + + // with lambdas + new OnlineBankingLambda() + .processCustomer(1, (Customer c) -> System.out.println("Hello " + c.getName())); + + } + + private static abstract class OnlineBanking { + void processCustomer(int id) { + Customer c = Database.getCustomerWithId(id); + makeCustomerHappy(c); + } + + abstract void makeCustomerHappy(Customer c); + + } + + private static class OnlineBankingLambda { + void processCustomer(int id, Consumer makeCustomerHappy) { + Customer c = Database.getCustomerWithId(id); + makeCustomerHappy.accept(c); + } + } + + // dummy Customer class + static private class Customer { + private int id = 1; + private String name = "Brian"; + + private String getName() { + return name; + } + } + + // dummy Datbase class + static private class Database { + static Customer getCustomerWithId(int id) { + return new Customer(); + } + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/effective/time/DateTimeExamples.java b/java8/src/main/java/com/brianway/learning/java8/effective/time/DateTimeExamples.java new file mode 100644 index 0000000..e3b63f0 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/effective/time/DateTimeExamples.java @@ -0,0 +1,162 @@ +package com.brianway.learning.java8.effective.time; + +import static java.time.temporal.TemporalAdjusters.lastDayOfMonth; +import static java.time.temporal.TemporalAdjusters.nextOrSame; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.chrono.JapaneseDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAdjuster; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * 新的时间和日期 API 示例 + */ +public class DateTimeExamples { + + private static final ThreadLocal formatters = new ThreadLocal() { + protected DateFormat initialValue() { + return new SimpleDateFormat("dd-MMM-yyyy"); + } + }; + + public static void main(String[] args) { + System.out.println("---------------useOldDate---------------------"); + useOldDate(); + System.out.println("---------------useLocalDate-----------------"); + useLocalDate(); + System.out.println("-------------useTemporalAdjuster---------------"); + useTemporalAdjuster(); + System.out.println("--------------useDateFormatter---------------"); + useDateFormatter(); + } + + private static void useOldDate() { + Date date = new Date(114, 2, 18); + System.out.println(date); + + System.out.println(formatters.get().format(date)); + + Calendar calendar = Calendar.getInstance(); + calendar.set(2014, Calendar.FEBRUARY, 18); + System.out.println(calendar); + } + + private static void useLocalDate() { + LocalDate date = LocalDate.of(2014, 3, 18); + int year = date.getYear(); // 2014 + Month month = date.getMonth(); // MARCH + int day = date.getDayOfMonth(); // 18 + DayOfWeek dow = date.getDayOfWeek(); // TUESDAY + int len = date.lengthOfMonth(); // 31 (days in March) + boolean leap = date.isLeapYear(); // false (not a leap year) + System.out.println(date); + + int y = date.get(ChronoField.YEAR); + int m = date.get(ChronoField.MONTH_OF_YEAR); + int d = date.get(ChronoField.DAY_OF_MONTH); + + LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20 + int hour = time.getHour(); // 13 + int minute = time.getMinute(); // 45 + int second = time.getSecond(); // 20 + System.out.println(time); + + LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 2014-03-18T13:45 + LocalDateTime dt2 = LocalDateTime.of(date, time); + LocalDateTime dt3 = date.atTime(13, 45, 20); + LocalDateTime dt4 = date.atTime(time); + LocalDateTime dt5 = time.atDate(date); + System.out.println(dt1); + + LocalDate date1 = dt1.toLocalDate(); + System.out.println(date1); + LocalTime time1 = dt1.toLocalTime(); + System.out.println(time1); + + Instant instant = Instant.ofEpochSecond(44 * 365 * 86400); + Instant now = Instant.now(); + + Duration d1 = Duration.between(LocalTime.of(13, 45, 10), time); + Duration d2 = Duration.between(instant, now); + System.out.println(d1.getSeconds()); + System.out.println(d2.getSeconds()); + + Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); + System.out.println(threeMinutes); + + JapaneseDate japaneseDate = JapaneseDate.from(date); + System.out.println(japaneseDate); + } + + private static void useTemporalAdjuster() { + LocalDate date = LocalDate.of(2014, 3, 18); + date = date.with(nextOrSame(DayOfWeek.SUNDAY)); + System.out.println(date); + date = date.with(lastDayOfMonth()); + System.out.println(date); + + date = date.with(new NextWorkingDay()); + System.out.println(date); + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + System.out.println(date); + date = date.with(new NextWorkingDay()); + System.out.println(date); + + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + System.out.println(date); + date = date.with(DateTimeExamples::nextWorkingDay); + System.out.println(date); + } + + private static class NextWorkingDay implements TemporalAdjuster { + @Override + public Temporal adjustInto(Temporal temporal) { + return nextWorkingDay(temporal); + } + } + + private static Temporal nextWorkingDay(Temporal temporal) { + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + int dayToAdd = 1; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); + } + + private static void useDateFormatter() { + LocalDate date = LocalDate.of(2014, 3, 18); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); + + System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE)); + System.out.println(date.format(formatter)); + System.out.println(date.format(italianFormatter)); + + DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder() + .appendText(ChronoField.DAY_OF_MONTH) + .appendLiteral(". ") + .appendText(ChronoField.MONTH_OF_YEAR) + .appendLiteral(" ") + .appendText(ChronoField.YEAR) + .parseCaseInsensitive() + .toFormatter(Locale.ITALIAN); + + System.out.println(date.format(complexFormatter)); + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/lambda/FilteringApples.java b/java8/src/main/java/com/brianway/learning/java8/lambda/FilteringApples.java index fcdfa85..bb730ea 100644 --- a/java8/src/main/java/com/brianway/learning/java8/lambda/FilteringApples.java +++ b/java8/src/main/java/com/brianway/learning/java8/lambda/FilteringApples.java @@ -34,6 +34,7 @@ public static void main(String[] args) { /** * 根据抽象条件筛选 * 将迭代集合的逻辑和要应用到集合中每个元素的行为区分开 + * * @param inventory * @param p * @return diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/Laziness.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/Laziness.java index 33d2a0e..fb0aac5 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/Laziness.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/Laziness.java @@ -15,7 +15,8 @@ public static void main(String[] args) { List twoEvenSquares = numbers.stream() .filter(n -> { - System.out.println("filtering " + n); return n % 2 == 0; + System.out.println("filtering " + n); + return n % 2 == 0; }) .map(n -> { System.out.println("mapping " + n); diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Filtering.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Filtering.java index 5f83f0c..859dd24 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Filtering.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Filtering.java @@ -16,38 +16,38 @@ */ public class Filtering { - public static void main(String...args){ + public static void main(String... args) { // Filtering with predicate List vegetarianMenu = - menu.stream() - .filter(Dish::isVegetarian) - .collect(toList()); + menu.stream() + .filter(Dish::isVegetarian) + .collect(toList()); vegetarianMenu.forEach(System.out::println); // Filtering unique elements List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream() - .filter(i -> i % 2 == 0) - .distinct() - .forEach(System.out::println); + .filter(i -> i % 2 == 0) + .distinct() + .forEach(System.out::println); // Truncating a stream List dishesLimit3 = - menu.stream() - .filter(d -> d.getCalories() > 300) - .limit(3) - .collect(toList()); + menu.stream() + .filter(d -> d.getCalories() > 300) + .limit(3) + .collect(toList()); dishesLimit3.forEach(System.out::println); // Skipping elements List dishesSkip2 = - menu.stream() - .filter(d -> d.getCalories() > 300) - .skip(2) - .collect(toList()); + menu.stream() + .filter(d -> d.getCalories() > 300) + .skip(2) + .collect(toList()); dishesSkip2.forEach(System.out::println); } diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Finding.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Finding.java index 94f80b7..25b4845 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Finding.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Finding.java @@ -10,32 +10,32 @@ */ public class Finding { - public static void main(String...args){ - if(isVegetarianFriendlyMenu()){ + public static void main(String... args) { + if (isVegetarianFriendlyMenu()) { System.out.println("Vegetarian friendly"); } System.out.println(isHealthyMenu()); System.out.println(isHealthyMenu2()); - + Optional dish = findVegetarianDish(); dish.ifPresent(d -> System.out.println(d.getName())); } - - private static boolean isVegetarianFriendlyMenu(){ + + private static boolean isVegetarianFriendlyMenu() { return menu.stream().anyMatch(Dish::isVegetarian); } - - private static boolean isHealthyMenu(){ + + private static boolean isHealthyMenu() { return menu.stream().allMatch(d -> d.getCalories() < 1000); } - - private static boolean isHealthyMenu2(){ + + private static boolean isHealthyMenu2() { return menu.stream().noneMatch(d -> d.getCalories() >= 1000); } - - private static Optional findVegetarianDish(){ + + private static Optional findVegetarianDish() { return menu.stream().filter(Dish::isVegetarian).findAny(); } - + } diff --git a/java8/src/test/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParamTest.java b/java8/src/test/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParamTest.java new file mode 100644 index 0000000..051e894 --- /dev/null +++ b/java8/src/test/java/com/brianway/learning/java8/effective/optional/ReadPositiveIntParamTest.java @@ -0,0 +1,32 @@ +package com.brianway.learning.java8.effective.optional; + +import static com.brianway.learning.java8.effective.optional.ReadPositiveIntParam.readDurationImperative; +import static com.brianway.learning.java8.effective.optional.ReadPositiveIntParam.readDurationWithOptional; +import static junit.framework.TestCase.assertEquals; +import org.junit.Test; + +import java.util.Properties; + +/** + * Created by brian on 17/3/27. + */ +public class ReadPositiveIntParamTest { + @Test + public void testMap() { + Properties props = new Properties(); + props.setProperty("a", "5"); + props.setProperty("b", "true"); + props.setProperty("c", "-3"); + + assertEquals(5, readDurationImperative(props, "a")); + assertEquals(0, readDurationImperative(props, "b")); + assertEquals(0, readDurationImperative(props, "c")); + assertEquals(0, readDurationImperative(props, "d")); + + assertEquals(5, readDurationWithOptional(props, "a")); + assertEquals(0, readDurationWithOptional(props, "b")); + assertEquals(0, readDurationWithOptional(props, "c")); + assertEquals(0, readDurationWithOptional(props, "d")); + } + +} diff --git a/pom.xml b/pom.xml index 3982c6d..5965258 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ UTF-8 1.8 1.8 - 4.11 + 4.13.1