diff --git a/README.md b/README.md index 025f51f..1c21ea6 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ - [java-multithread](/java-multithread):多线程模块的 Java 源码 - [java-container](/java-container):容器类模块的 Java 源码 - [java-io](/java-io):IO 模块的 Java 源码 +- [java-jvm](/java-jvm): JVM 模块的 Java 源码 +- [java8](/java8): Java 8 模块的源码 # 博客文档 @@ -54,17 +56,24 @@ ----- +## 赞助 + +如果您觉得该项目对您有帮助,请扫描下方二维码对我进行鼓励,以便我更好的维护和更新,谢谢支持! + +![支付宝](https://brianway.github.io/img/alipay_small.png) +![微信](https://brianway.github.io/img/wechatpay_small.png) + # TODO 计划将这个仓库进行重构,逐步扩充并实现下面的功能。 -* [x] 整理成 maven 的结构,使用聚合和继承特性(2016.4.12完成) +* [x] 整理成 maven 的结构,使用聚合和继承特性(2016.4.12 完成) * [ ] 原有的 Java SE 部分代码重构为 java-base 模块,并逐步上传代码 -* [x] 多线程部分使用 java-multithread 模块(2016.4.17完成雏形) +* [x] 多线程部分使用 java-multithread 模块(2016.4.17 完成雏形) * [ ] 容器类部分使用模块 java-container * [ ] IO 部分使用模块 java-io -* [ ] Java 虚拟机相关部分使用模块 java-jvm -* [ ] Java 8 新特性使用模块 java8(正在进行) +* [x] Java 虚拟机相关部分使用模块 java-jvm(2017.3.20 完成雏形) +* [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/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClassLoaderTest.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClassLoaderTest.java new file mode 100644 index 0000000..6c6c289 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClassLoaderTest.java @@ -0,0 +1,40 @@ +package com.brianway.learning.java.jvm.classloading; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by brian on 17/3/20. + * 不同的类加载器对 instanceof 关键字运算结果的影响 + */ +public class ClassLoaderTest { + public static void main(String[] args) throws Exception { + ClassLoader myLoader = new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; + + InputStream is = getClass().getResourceAsStream(fileName); + if (is == null) { + return super.loadClass(name); + } + byte[] b = new byte[is.available()]; + is.read(b); + return defineClass(name, b, 0, b.length); + } catch (IOException e) { + throw new ClassNotFoundException(name); + } + } + }; + + Object obj = myLoader.loadClass("com.brianway.learning.java.jvm.classloading.ClassLoaderTest").newInstance(); + System.out.println(obj.getClass()); + System.out.println(obj instanceof com.brianway.learning.java.jvm.classloading.ClassLoaderTest); + } +} + +/* +class com.brianway.learning.java.jvm.classloading.ClassLoaderTest +false + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClinitOrder.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClinitOrder.java new file mode 100644 index 0000000..365a2e7 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ClinitOrder.java @@ -0,0 +1,27 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/20. + * () 方法执行顺序 + */ +public class ClinitOrder { + static class Parent { + public static int A = 1; + + static { + System.out.println("execute before A = 2, A now is " + A); + A = 2; + } + } + + static class Sub extends Parent { + public static int B = A; + } + + + public static void main(String[] args) { + System.out.println(Sub.B); + } +} + + diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ConstClass.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ConstClass.java new file mode 100644 index 0000000..9338623 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/ConstClass.java @@ -0,0 +1,15 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/19. + * 被动使用类字段演示 + * 常量在编译阶段会存入调用类的常量池,本质上没有引用调用常量的类, + * 不会触发定义常量的类的初始化 + */ +public class ConstClass { + static { + System.out.println("ConsClass init!"); + } + + public static final String HELLOWORLD = "hello world"; +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/DeadLoopClass.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/DeadLoopClass.java new file mode 100644 index 0000000..f2ec554 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/DeadLoopClass.java @@ -0,0 +1,30 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/20. + * 字段解析 + * 多个线程同时初始化一个类,只有一个线程会执行 ()方法,其他被阻塞 + */ +public class DeadLoopClass { + static { + if (true) { + System.out.println(Thread.currentThread() + " init DeadLoopClass"); + while (true) { + + } + } + } + + public static void main(String[] args) { + Runnable script = () -> { + System.out.println(Thread.currentThread() + " start"); + DeadLoopClass dlc = new DeadLoopClass(); + System.out.println(Thread.currentThread() + " run over"); + }; + + Thread thread1 = new Thread(script); + Thread thread2 = new Thread(script); + thread1.start(); + thread2.start(); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/FieldResolution.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/FieldResolution.java new file mode 100644 index 0000000..04ecfdb --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/FieldResolution.java @@ -0,0 +1,32 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/20. + * 字段解析 + * 如果注释了 Sub 类中的 "public static int A = 4",会编译错误 + */ +public class FieldResolution { + interface Interface0 { + int A = 0; + } + + interface Interface1 extends Interface0 { + int A = 1; + } + + interface Interface2 { + int A = 2; + } + + static class Parent implements Interface1 { + public static int A = 3; + } + + static class Sub extends Parent implements Interface2 { + public static int A = 4;// 注释此句试试 + } + + public static void main(String[] args) { + System.out.println(Sub.A); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/NoInitialization.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/NoInitialization.java new file mode 100644 index 0000000..4efbf0f --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/NoInitialization.java @@ -0,0 +1,21 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/19. + * 非主动使用类字段演示 + * 1. 静态字段只有直接定义这个字段的类才会被初始化 + * 2. 通过数组来定义引用类,不会触发类的初始化 + * (-XX:+TraceClassLoading,对于 HotSpot 虚拟机可观察到子类的加载) + */ +public class NoInitialization { + public static void main(String[] args) { + System.out.println(SubClass.value); + SubClass[] sca = new SubClass[10]; + + System.out.println(ConstClass.HELLOWORLD); + } +} +/* +SuperClass init! +123 + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SimpleClass.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SimpleClass.java new file mode 100644 index 0000000..fc00360 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SimpleClass.java @@ -0,0 +1,14 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/15. + * 一段简单的 Java 代码,用于分析字节码 + * javap -verbose SimpleClass.java + */ +public class SimpleClass { + private int m; + + public int inc() { + return m + 1; + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SubClass.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SubClass.java new file mode 100644 index 0000000..a15c620 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SubClass.java @@ -0,0 +1,10 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/19. + */ +public class SubClass extends SuperClass { + static { + System.out.println("SubClass init!"); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SuperClass.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SuperClass.java new file mode 100644 index 0000000..6b83c5c --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/classloading/SuperClass.java @@ -0,0 +1,14 @@ +package com.brianway.learning.java.jvm.classloading; + +/** + * Created by brian on 17/3/19. + * 被动引用的例子 + * 通过子类引用父类的静态字段,不会导致子类初始化 + */ +public class SuperClass { + static { + System.out.println("SuperClass init!"); + } + + public static int value = 123; +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/GrandFatherCaller.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/GrandFatherCaller.java new file mode 100644 index 0000000..5c5fa41 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/GrandFatherCaller.java @@ -0,0 +1,45 @@ +package com.brianway.learning.java.jvm.dynamictype; + +import static java.lang.invoke.MethodHandles.lookup; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +/** + * Created by brian on 17/3/20. + * 使用 MethodHandle 来访问祖类方法 + * TODO 为什么打印出来的是 "i am father" 而不是 "i am grandfather"? + * 参考: + * https://www.zhihu.com/question/57379777 + * https://www.zhihu.com/question/40427344/answer/86545388 + */ +public class GrandFatherCaller { + class GrandFather { + void thinking() { + System.out.println("i am grandfather"); + } + } + + class Father extends GrandFather { + void thinking() { + System.out.println("i am father"); + } + } + + class Son extends Father { + void thinking() { + try { + MethodType mt = MethodType.methodType(void.class); + MethodHandle mh = lookup().findSpecial( + GrandFather.class, "thinking", mt, Son.class); + mh.invoke(this); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + } + + public static void main(String[] args) { + (new GrandFatherCaller().new Son()).thinking(); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/InvokeDynamicTest.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/InvokeDynamicTest.java new file mode 100644 index 0000000..5d5fb01 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/InvokeDynamicTest.java @@ -0,0 +1,53 @@ +package com.brianway.learning.java.jvm.dynamictype; + +import static java.lang.invoke.MethodHandles.lookup; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** + * Created by brian on 17/3/20. + * invokedynamic 指令演示 + */ +public class InvokeDynamicTest { + + public static void main(String[] args) throws Throwable { + INDY_BootstrapMethod().invokeExact("brianway"); + } + + public static void testMethod(String s) { + System.out.println("hello String:" + s); + } + + public static CallSite BootstrapMethod( + MethodHandles.Lookup lookup, String name, MethodType mt) + throws Throwable { + return new ConstantCallSite(lookup.findStatic(InvokeDynamicTest.class, name, mt)); + } + + private static MethodType MT_BootstrapMethod() { + return MethodType.fromMethodDescriptorString( + "(Ljava/lang/invoke/MethodHandles$Lookup;" + + "Ljava/lang/String;Ljava/lang/invoke/MethodType;)" + + "Ljava/lang/invoke/CallSite;", null + ); + } + + private static MethodHandle MH_BootstrapMethod() throws Throwable { + return lookup().findStatic(InvokeDynamicTest.class, "BootstrapMethod", + MT_BootstrapMethod()); + } + + private static MethodHandle INDY_BootstrapMethod() throws Throwable { + CallSite cs = (CallSite) MH_BootstrapMethod().invokeWithArguments( + lookup(), + "testMethod", + MethodType.fromMethodDescriptorString( + "(Ljava/lang/String;)V", null + )); + return cs.dynamicInvoker(); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/MethodHandleTest.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/MethodHandleTest.java new file mode 100644 index 0000000..0d977a5 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/dynamictype/MethodHandleTest.java @@ -0,0 +1,31 @@ +package com.brianway.learning.java.jvm.dynamictype; + +import static java.lang.invoke.MethodHandles.lookup; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +/** + * Created by brian on 17/3/20. + * MethodHandle 演示 + */ +public class MethodHandleTest { + static class ClassA { + public void println(String s) { + System.out.println("classA " + s); + } + } + + public static void main(String[] args) throws Throwable { + Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA(); + getPrintlnMH(obj).invokeExact("brianway"); + } + + private static MethodHandle getPrintlnMH(Object receiver) throws Throwable { + // 方法类型 + MethodType mt = MethodType.methodType(void.class, String.class); + + return lookup().findVirtual(receiver.getClass(), "println", mt) + .bindTo(receiver); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/FinalizeEscapeGC.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/FinalizeEscapeGC.java new file mode 100644 index 0000000..810caf1 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/FinalizeEscapeGC.java @@ -0,0 +1,58 @@ +package com.brianway.learning.java.jvm.gc; + +/** + * Created by brian on 17/3/3. + * 对象可以在被 GC 时自我拯救 + * 这种自救的机会只有一次,因为一个对象的 finalize() 方法最多只会被系统自动调用一次 + */ +public class FinalizeEscapeGC { + public static FinalizeEscapeGC SAVE_HOOK = null; + + public void isAlive() { + System.out.println("yes, i am still alive :)"); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + System.out.println("finalize method executed"); + FinalizeEscapeGC.SAVE_HOOK = this; + } + + public static void main(String[] args) throws Throwable { + SAVE_HOOK = new FinalizeEscapeGC(); + + //对象第一次成功拯救自己 + SAVE_HOOK = null; + System.gc(); + + // finalize 优先级低,暂停 0.5 秒以等待 + Thread.sleep(500); + + if (SAVE_HOOK != null) { + SAVE_HOOK.isAlive(); + } else { + System.out.println("no, i ma dead :("); + } + + //与上面完全相同,缺失败 + SAVE_HOOK = null; + System.gc(); + + // finalize 优先级低,暂停 0.5 秒以等待 + Thread.sleep(500); + + if (SAVE_HOOK != null) { + SAVE_HOOK.isAlive(); + } else { + System.out.println("no, i ma dead :("); + } + + } +} + +/* +finalize method executed +yes, i am still alive :) +no, i ma dead :( + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/ReferenceCountingGC.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/ReferenceCountingGC.java new file mode 100644 index 0000000..2f15e09 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/gc/ReferenceCountingGC.java @@ -0,0 +1,31 @@ +package com.brianway.learning.java.jvm.gc; + +/** + * Created by brian on 17/3/2. + * 引用计数算法缺陷 + */ +public class ReferenceCountingGC { + public Object instance = null; + private static final int _1MB = 1024 * 1024; + + /** + * 占点内存,以便 GC 日志观看 + */ + private byte[] bigSize = new byte[2 * _1MB]; + + public static void main(String[] args) { + testGC(); + } + + public static void testGC() { + ReferenceCountingGC objA = new ReferenceCountingGC(); + ReferenceCountingGC objB = new ReferenceCountingGC(); + objA.instance = objB; + objB.instance = objA; + + objA = null; + objB = null; + + System.gc(); + } +} diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/Dispatch.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/Dispatch.java new file mode 100644 index 0000000..ffb9f59 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/Dispatch.java @@ -0,0 +1,45 @@ +package com.brianway.learning.java.jvm.methodinvoke; + +/** + * Created by brian on 17/3/20. + * 单分派、多分派演示 + */ +public class Dispatch { + static class QQ { + } + + static class _360 { + } + + public static class Father { + public void hardChoice(QQ arg) { + System.out.println("father choose qq"); + } + + public void hardChoice(_360 arg) { + System.out.println("father choose 360"); + } + } + + public static class Son extends Father { + public void hardChoice(QQ arg) { + System.out.println("son choose qq"); + } + + public void hardChoice(_360 arg) { + System.out.println("son choose 360"); + } + } + + public static void main(String[] args) { + Father father = new Father(); + Father son = new Son(); + father.hardChoice(new _360()); + son.hardChoice(new QQ()); + } +} + +/* +father choose 360 +son choose qq + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/DynamicDispatch.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/DynamicDispatch.java new file mode 100644 index 0000000..1088fa3 --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/DynamicDispatch.java @@ -0,0 +1,41 @@ +package com.brianway.learning.java.jvm.methodinvoke; + +/** + * Created by brian on 17/3/20. + * 方法动态分派演示 + */ +public class DynamicDispatch { + static abstract class Human { + protected abstract void sayHello(); + } + + static class Man extends Human { + @Override + protected void sayHello() { + System.out.println("man say hello"); + } + } + + static class Woman extends Human { + @Override + protected void sayHello() { + System.out.println("woman say hello"); + } + } + + public static void main(String[] args) { + Human man = new Man(); + Human woman = new Woman(); + man.sayHello(); + woman.sayHello(); + + man = new Woman(); + man.sayHello(); + } +} + +/* +man say hello +woman say hello +woman say hello + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticDispatch.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticDispatch.java new file mode 100644 index 0000000..c0b644f --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticDispatch.java @@ -0,0 +1,47 @@ +package com.brianway.learning.java.jvm.methodinvoke; + +/** + * Created by brian on 17/3/20. + * 方法静态分派演示 + * Method Overload Resolution + */ +public class StaticDispatch { + static abstract class Human { + } + + static class Man extends Human { + } + + static class Woman extends Human { + } + + public void sayHello(Human guy) { + System.out.println("hello,guy!"); + } + + public void sayHello(Man guy) { + System.out.println("hello,gentleman!"); + } + + public void sayHello(Woman guy) { + System.out.println("hello,lady!"); + } + + public static void main(String[] args) { + Human man = new Man(); + Human woman = new Woman(); + StaticDispatch sr = new StaticDispatch(); + + sr.sayHello(man); + sr.sayHello(woman); + sr.sayHello((Man)man); + sr.sayHello((Woman) woman); + } +} + +/* +hello,guy! +hello,guy! +hello,gentleman! +hello,lady! + */ \ No newline at end of file diff --git a/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticResolution.java b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticResolution.java new file mode 100644 index 0000000..c5df56e --- /dev/null +++ b/java-jvm/src/main/java/com/brianway/learning/java/jvm/methodinvoke/StaticResolution.java @@ -0,0 +1,17 @@ +package com.brianway.learning.java.jvm.methodinvoke; + +/** + * Created by brian on 17/3/20. + * 方法静态解析演示 + * javac -verbose StaticResolution.java + * javap -verbose StaticResolution + */ +public class StaticResolution { + public static void sayHello() { + System.out.println("hello world"); + } + + public static void main(String[] args) { + StaticResolution.sayHello(); + } +} diff --git a/java8/README.md b/java8/README.md index e0dae1e..542462c 100644 --- a/java8/README.md +++ b/java8/README.md @@ -11,6 +11,76 @@ ## steam API -- Dish: +- Dish: 菜肴类,有一个静态属性的菜肴列表 - StreamBasic: 指令式和函数式分别挑选低热量食物名 -- StreamOperation: 流操作:中间操作和终端操作 +- StreamOperation: 流操作(中间操作和终端操作) +- Laziness: 流操作-中间操作 +- BuildingStreams: 构建流,从值序列,数组,文件来创建流。斐波拉切数列的几种生成方法 + + +使用流 + +- Filtering: 筛选(谓词筛选:filter;筛选互异的元素:distinct;忽略头几个元素:limit;截短至指定长度:skip) +- Mapping: 映射 +- Finding: 查找和匹配 +- Reducing: 归约 +- PuttingIntoPractice: 各种流操作的使用示例 +- NumericStreams: 数值流(原始类型特化,数值范围) + +用流收集数据 + +- Summarizing: 归约和汇总 +- Reducing: 收集器的灵活性,以不同的方法执行相同的操作 +- Grouping: 分组 +- Partitioning: 分区 +- GroupingTransactions: 分别使用指令式和函数式进行分组 +- ToListCollector: 自定义 ToListCollector,以及进行自定义收集而不去实现 Collector +- PartitionPrimeNumbers: 将数字按质数和非质数分区;Collector 详解 +- CollectorHarness: 比较收集器的性能 + + +并行数据处理 + +- ParallelStreamsHarness: 测量流性能 +- 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/BuildingStreams.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/BuildingStreams.java index 13d0cd3..e98499d 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/BuildingStreams.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/BuildingStreams.java @@ -9,12 +9,16 @@ import java.util.stream.Stream; /** + * 构建流:从值序列,数组,文件来创建流 * 斐波拉切数列的几种生成方法 */ public class BuildingStreams { public static void main(String... args) throws Exception { + /** + * 由值创建流 + */ // Stream.of Stream stream = Stream.of("Java 8", "Lambdas", "In", "Action"); stream.map(String::toUpperCase).forEach(System.out::println); @@ -22,10 +26,16 @@ public static void main(String... args) throws Exception { // Stream.empty Stream emptyStream = Stream.empty(); + /** + * 由数组创建流 + */ // Arrays.stream int[] numbers = {2, 3, 5, 7, 11, 13}; System.out.println(Arrays.stream(numbers).sum()); + /** + * 由函数生成流:创建无限流 + */ // Stream.iterate Stream.iterate(0, n -> n + 2) .limit(10) @@ -68,6 +78,9 @@ public int getAsInt() { }; IntStream.generate(fib).limit(10).forEach(System.out::println); + /** + * 由文件生成流 + */ String path = BuildingStreams.class.getResource("/").getPath() + "/data-building.txt"; long uniqueWords = Files.lines(Paths.get(path), Charset.defaultCharset()) .flatMap(line -> Arrays.stream(line.split(" "))) 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/StreamBasic.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamBasic.java index 7d8656c..828434c 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamBasic.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamBasic.java @@ -24,6 +24,9 @@ public static void main(String... args) { } + /** + * 指令式 + */ public static List getLowCaloricDishesNamesInJava7(List dishes) { List lowCaloricDishes = new ArrayList<>(); for (Dish d : dishes) { @@ -43,6 +46,9 @@ public int compare(Dish d1, Dish d2) { return lowCaloricDishesName; } + /** + * 函数式 + */ public static List getLowCaloricDishesNamesInJava8(List dishes) { return dishes.stream() .filter(d -> d.getCalories() < 400) diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamOperation.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamOperation.java index 9ed02f1..19e3e77 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamOperation.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/StreamOperation.java @@ -26,5 +26,9 @@ public static void main(String[] args) { .limit(3) .collect(Collectors.toList()); System.out.println(names); + + //forEach 是一个返回 void 的终端操作 + System.out.println("------forEach-------"); + menu.stream().forEach(System.out::println); } } diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/CollectorHarness.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/CollectorHarness.java similarity index 94% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/CollectorHarness.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/collect/CollectorHarness.java index 3a5e940..af91a7c 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/CollectorHarness.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/CollectorHarness.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.collect; import java.util.function.Consumer; diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Grouping.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Grouping.java new file mode 100644 index 0000000..608c2b1 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Grouping.java @@ -0,0 +1,110 @@ +package com.brianway.learning.java8.streamapi.collect; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; +import static java.util.stream.Collectors.maxBy; +import static java.util.stream.Collectors.summingInt; +import static java.util.stream.Collectors.toSet; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * 分组 + */ +public class Grouping { + + private enum CaloricLevel { + DIET, + NORMAL, + FAT + } + + public static void main(String... args) { + System.out.println("Dishes grouped by type: " + groupDishesByType()); + System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel()); + System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel()); + System.out.println("Count dishes in groups: " + countDishesInGroups()); + System.out.println("Most caloric dishes by type: " + mostCaloricDishesByType()); + System.out.println("Most caloric dishes by type: " + mostCaloricDishesByTypeWithoutOprionals()); + System.out.println("Sum calories by type: " + sumCaloriesByType()); + System.out.println("Caloric levels by type: " + caloricLevelsByType()); + } + + private static CaloricLevel getCaloricLevel(Dish dish) { + if (dish.getCalories() <= 400) { + return CaloricLevel.DIET; + } else if (dish.getCalories() <= 700) { + return CaloricLevel.NORMAL; + } else { + return CaloricLevel.FAT; + } + } + + private static Map> groupDishesByType() { + return menu.stream().collect(groupingBy(Dish::getType)); + } + + private static Map> groupDishesByCaloricLevel() { + return menu.stream().collect( + groupingBy(Grouping::getCaloricLevel)); + } + + /** + * 多级分组 + */ + private static Map>> groupDishedByTypeAndCaloricLevel() { + return menu.stream().collect( + groupingBy(Dish::getType, + groupingBy(Grouping::getCaloricLevel) + ) + ); + } + + /** + * 按子数组收集数据 + */ + private static Map countDishesInGroups() { + return menu.stream().collect(groupingBy(Dish::getType, counting())); + } + + private static Map> mostCaloricDishesByType() { + return menu.stream().collect( + groupingBy(Dish::getType, + maxBy(Comparator.comparingInt(Dish::getCalories))) + ); + } + + /** + * 按子数组收集数据,把收集器的结果转换为另一种类型 + */ + private static Map mostCaloricDishesByTypeWithoutOprionals() { + return menu.stream().collect( + groupingBy(Dish::getType, + collectingAndThen( + maxBy(Comparator.comparingInt(Dish::getCalories)), + Optional::get))); + } + + /** + * 与 groupingBy 联合使用的其他收集器例子 + */ + private static Map sumCaloriesByType() { + return menu.stream().collect(groupingBy(Dish::getType, + summingInt(Dish::getCalories))); + } + + private static Map> caloricLevelsByType() { + return menu.stream().collect( + groupingBy(Dish::getType, mapping( + Grouping::getCaloricLevel, + toSet()))); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/GroupingTransactions.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/GroupingTransactions.java similarity index 97% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/GroupingTransactions.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/collect/GroupingTransactions.java index 35a122e..47aad70 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/GroupingTransactions.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/GroupingTransactions.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.collect; import static java.util.stream.Collectors.groupingBy; diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/PartitionPrimeNumbers.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/PartitionPrimeNumbers.java similarity index 93% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/PartitionPrimeNumbers.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/collect/PartitionPrimeNumbers.java index 1a06c84..2a29b40 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/PartitionPrimeNumbers.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/PartitionPrimeNumbers.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.collect; import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH; import static java.util.stream.Collectors.partitioningBy; @@ -39,6 +39,9 @@ public static Map> partitionPrimes(int n) { .collect(partitioningBy(PartitionPrimeNumbers::isPrime)); } + /** + * 判断一个数是不是质数 + */ public static boolean isPrime(int candidate) { return IntStream.rangeClosed(2, candidate - 1) .limit((long) Math.floor(Math.sqrt((double) candidate)) - 1) @@ -49,12 +52,22 @@ public static Map> partitionPrimesWithCustomCollector(int return IntStream.rangeClosed(2, n).boxed().collect(new PrimeNumbersCollector()); } + /** + * 仅用质数作除数 + * + * @param primes 部分结果 + * @param candidate 要判断的数 + * @return + */ public static boolean isPrime(List primes, Integer candidate) { double candidateRoot = Math.sqrt((double) candidate); //return primes.stream().filter(p -> p < candidateRoot).noneMatch(p -> candidate % p == 0); return takeWhile(primes, i -> i <= candidateRoot).stream().noneMatch(i -> candidate % i == 0); } + /** + * 返回元素满足谓词的最长前缀 + */ public static List takeWhile(List list, Predicate p) { int i = 0; for (A item : list) { diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Partitioning.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Partitioning.java new file mode 100644 index 0000000..8810f88 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Partitioning.java @@ -0,0 +1,42 @@ +package com.brianway.learning.java8.streamapi.collect; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; +import static java.util.Comparator.comparingInt; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.maxBy; +import static java.util.stream.Collectors.partitioningBy; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 分区 + */ +public class Partitioning { + + public static void main(String... args) { + System.out.println("Dishes partitioned by vegetarian: " + partitionByVegeterian()); + System.out.println("Vegetarian Dishes by type: " + vegetarianDishesByType()); + System.out.println("Most caloric dishes by vegetarian: " + mostCaloricPartitionedByVegetarian()); + } + + private static Map> partitionByVegeterian() { + return menu.stream().collect(partitioningBy(Dish::isVegetarian)); + } + + private static Map>> vegetarianDishesByType() { + return menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType))); + } + + private static Object mostCaloricPartitionedByVegetarian() { + return menu.stream().collect( + partitioningBy(Dish::isVegetarian, + collectingAndThen( + maxBy(comparingInt(Dish::getCalories)), + Optional::get))); + } +} + diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/Reducing.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Reducing.java similarity index 86% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/Reducing.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Reducing.java index d0721c6..11f4bc8 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/Reducing.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Reducing.java @@ -1,8 +1,12 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.collect; +import com.brianway.learning.java8.streamapi.Dish; import static com.brianway.learning.java8.streamapi.Dish.menu; import static java.util.stream.Collectors.reducing; +/** + * 收集器的灵活性,以不同的方法执行相同的操作 + */ public class Reducing { public static void main(String... args) { diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Summarizing.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Summarizing.java new file mode 100644 index 0000000..a1ea80c --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/Summarizing.java @@ -0,0 +1,86 @@ +package com.brianway.learning.java8.streamapi.collect; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; +import static java.util.stream.Collectors.averagingInt; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.reducing; +import static java.util.stream.Collectors.summarizingInt; +import static java.util.stream.Collectors.summingInt; + +import java.util.Comparator; +import java.util.IntSummaryStatistics; +import java.util.function.BinaryOperator; + +/** + * 归约和汇总 + */ +public class Summarizing { + + public static void main(String... args) { + System.out.println("Nr. of dishes: " + howManyDishes()); + System.out.println("The most caloric dish is: " + findMostCaloricDish()); + System.out.println("The most caloric dish is: " + findMostCaloricDishUsingComparator()); + System.out.println("Total calories in menu: " + calculateTotalCalories()); + System.out.println("Average calories in menu: " + calculateAverageCalories()); + System.out.println("Menu statistics: " + calculateMenuStatistics()); + System.out.println("Short menu: " + getShortMenu()); + System.out.println("Short menu comma separated: " + getShortMenuCommaSeparated()); + } + + /** + * 统计个数 + */ + private static long howManyDishes() { + return menu.stream().collect(counting()); + } + + /** + * 查找流中最大值 + */ + private static Dish findMostCaloricDish() { + return menu.stream().collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)).get(); + } + + /** + * 查找流中最大值 + */ + private static Dish findMostCaloricDishUsingComparator() { + Comparator dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories); + BinaryOperator moreCaloricOf = BinaryOperator.maxBy(dishCaloriesComparator); + return menu.stream().collect(reducing(moreCaloricOf)).get(); + } + + /** + * 汇总-求和 + */ + private static int calculateTotalCalories() { + return menu.stream().collect(summingInt(Dish::getCalories)); + } + + /** + * 汇总-求平均数 + */ + private static Double calculateAverageCalories() { + return menu.stream().collect(averagingInt(Dish::getCalories)); + } + + /** + * 汇总-统计值 + */ + private static IntSummaryStatistics calculateMenuStatistics() { + return menu.stream().collect(summarizingInt(Dish::getCalories)); + } + + /** + * 连接字符串 + */ + private static String getShortMenu() { + return menu.stream().map(Dish::getName).collect(joining()); + } + + private static String getShortMenuCommaSeparated() { + return menu.stream().map(Dish::getName).collect(joining(", ")); + } +} diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/ToListCollector.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/ToListCollector.java similarity index 63% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/ToListCollector.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/collect/ToListCollector.java index 68c64d0..dd5780e 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/ToListCollector.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/collect/ToListCollector.java @@ -1,5 +1,6 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.collect; +import com.brianway.learning.java8.streamapi.Dish; import static com.brianway.learning.java8.streamapi.Dish.menu; import static java.util.stream.Collector.Characteristics.CONCURRENT; import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH; @@ -18,24 +19,45 @@ /** * 自定义 ToListCollector + * 1.建立新的结果容器: supplier 方法 + * 2.将元素添到结果容器: accumulator 方法 + * 3.对容器应用最终转换: finisher 方法 + * 4.合并两个结果容器: combiner + * 5.characteristics 方法 */ public class ToListCollector implements Collector, List> { + /** + * 创建集合操作的起始点 + */ @Override public Supplier> supplier() { - return () -> new ArrayList(); + //return () -> new ArrayList(); + return ArrayList::new; } + /** + * 累积遍历过的项目,原位修改累加器 + */ @Override public BiConsumer, T> accumulator() { - return (list, item) -> list.add(item); + //return (list, item) -> list.add(item); + return List::add; } + /** + * 恒等函数 + */ @Override public Function, List> finisher() { - return i -> i; + //return i -> i; + return Function.identity(); } + /** + * 修改第一个累加器,将其与第二个累加器的内容合并 + * 返回第一个累加器 + */ @Override public BinaryOperator> combiner() { return (list1, list2) -> { @@ -44,6 +66,9 @@ public BinaryOperator> combiner() { }; } + /** + * 为收集器添加 IDENTITY_FINISH 和 CONCURRENT 标志 + */ @Override public Set characteristics() { return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT)); @@ -54,6 +79,7 @@ public static void main(String[] args) { System.out.println(dishes); dishes = menu.stream().collect(new ToListCollector<>()); System.out.println(dishes); + // 进行自定义收集而不去实现 Collector dishes = menu.stream().collect( ArrayList::new, List::add, diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreams.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreams.java index ac16867..78acf26 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreams.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreams.java @@ -9,6 +9,9 @@ */ public class ParallelStreams { + /** + * for 循环的迭代 + */ public static long iterativeSum(long n) { long result = 0; for (long i = 0; i <= n; i++) { @@ -21,10 +24,20 @@ public static long sequentialSum(long n) { return Stream.iterate(1L, i -> i + 1).limit(n).reduce(Long::sum).get(); } + /** + * iterate 的问题: + * - iterate 生成的是装箱的对象,必须拆成数字才能求和 + * - 很难把 iterate 分成多个独立块来并行执行 + */ public static long parallelSum(long n) { return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(Long::sum).get(); } + /** + * 使用 LongStream.rangeClosed + * - 没有装箱拆箱 + * - 容易拆分为独立的小块 + */ public static long rangedSum(long n) { return LongStream.rangeClosed(1, n).reduce(Long::sum).getAsLong(); } @@ -39,6 +52,9 @@ public static long sideEffectSum(long n) { return accumulator.total; } + /** + * 共享了可变状态,导致每次结果不一致 + */ public static long sideEffectParallelSum(long n) { Accumulator accumulator = new Accumulator(); LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add); diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreamsHarness.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreamsHarness.java index 06975b7..cbdb86e 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreamsHarness.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/parallel/ParallelStreamsHarness.java @@ -3,6 +3,9 @@ import java.util.concurrent.ForkJoinPool; import java.util.function.Function; +/** + * 测量流性能 + */ public class ParallelStreamsHarness { public static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool(); diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/PuttingIntoPractice.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/PuttingIntoPractice.java similarity index 97% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/PuttingIntoPractice.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/practice/PuttingIntoPractice.java index c27f3ed..d9384ec 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/PuttingIntoPractice.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/PuttingIntoPractice.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.practice; import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; @@ -6,6 +6,9 @@ import java.util.Arrays; import java.util.List; +/** + * 各种流操作的使用示例 + */ public class PuttingIntoPractice { public static void main(String... args) { Trader raoul = new Trader("Raoul", "Cambridge"); diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/Trader.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Trader.java similarity index 89% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/Trader.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Trader.java index b008b12..059c1d7 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/Trader.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Trader.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.practice; public class Trader { diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/Transaction.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Transaction.java similarity index 91% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/Transaction.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Transaction.java index 656149c..f9c4458 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/Transaction.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/practice/Transaction.java @@ -1,4 +1,4 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.practice; public class Transaction { 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 new file mode 100644 index 0000000..859dd24 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Filtering.java @@ -0,0 +1,54 @@ +package com.brianway.learning.java8.streamapi.usage; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; +import static java.util.stream.Collectors.toList; + +import java.util.Arrays; +import java.util.List; + +/** + * 筛选 + * - 谓词筛选:filter + * - 筛选互异的元素:distinct + * - 忽略头几个元素:limit + * - 截短至指定长度:skip + */ +public class Filtering { + + public static void main(String... args) { + + // Filtering with predicate + List vegetarianMenu = + 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); + + // Truncating a stream + List dishesLimit3 = + 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()); + + 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 new file mode 100644 index 0000000..25b4845 --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Finding.java @@ -0,0 +1,41 @@ +package com.brianway.learning.java8.streamapi.usage; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; + +import java.util.Optional; + +/** + * 查找和匹配 + */ +public class Finding { + + 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() { + return menu.stream().anyMatch(Dish::isVegetarian); + } + + private static boolean isHealthyMenu() { + return menu.stream().allMatch(d -> d.getCalories() < 1000); + } + + private static boolean isHealthyMenu2() { + return menu.stream().noneMatch(d -> d.getCalories() >= 1000); + } + + private static Optional findVegetarianDish() { + return menu.stream().filter(Dish::isVegetarian).findAny(); + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/Mapping.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Mapping.java similarity index 70% rename from java8/src/main/java/com/brianway/learning/java8/streamapi/Mapping.java rename to java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Mapping.java index 74f63c2..4187a4a 100644 --- a/java8/src/main/java/com/brianway/learning/java8/streamapi/Mapping.java +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Mapping.java @@ -1,5 +1,6 @@ -package com.brianway.learning.java8.streamapi; +package com.brianway.learning.java8.streamapi.usage; +import com.brianway.learning.java8.streamapi.Dish; import static com.brianway.learning.java8.streamapi.Dish.menu; import static java.util.stream.Collectors.toList; @@ -9,6 +10,7 @@ /** * 使用流-映射 * 注意扁平映射 + * flatMap 的效果:各个数组不是分别映射成一个流,而是映射成 流的内容 */ public class Mapping { @@ -44,5 +46,15 @@ public static void main(String... args) { .filter(pair -> (pair[0] + pair[1]) % 3 == 0) .collect(toList()); pairs.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")")); + + // flatMap + List pairs2 = numbers1.stream() + .flatMap(i -> numbers2.stream() + .filter(j -> (i + j) % 3 == 0) + .map(j -> new int[] {i, j}) + ) + .collect(toList()); + System.out.println("----------"); + pairs2.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")")); } } diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/NumericStreams.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/NumericStreams.java new file mode 100644 index 0000000..ec6888a --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/NumericStreams.java @@ -0,0 +1,71 @@ +package com.brianway.learning.java8.streamapi.usage; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; + +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * 数值流 + * 原始类型特化 + * 数值范围 + */ +public class NumericStreams { + + public static void main(String... args) { + + List numbers = Arrays.asList(3, 4, 5, 1, 2); + + Arrays.stream(numbers.toArray()).forEach(System.out::println); + int calories = menu.stream() + .mapToInt(Dish::getCalories) + .sum(); + System.out.println("Number of calories:" + calories); + + // max and OptionalInt + OptionalInt maxCalories = menu.stream() + .mapToInt(Dish::getCalories) + .max(); + + int max; + if (maxCalories.isPresent()) { + max = maxCalories.getAsInt(); + } else { + // we can choose a default value + max = 1; + } + System.out.println(max); + + // 直接使用 orElse + System.out.print("使用 orElse: "); + System.out.println(maxCalories.orElse(1)); + + // numeric ranges + IntStream evenNumbers = IntStream.rangeClosed(1, 100) + .filter(n -> n % 2 == 0); + + System.out.println(evenNumbers.count()); + + // 勾股数 + Stream pythagoreanTriples = + IntStream.rangeClosed(1, 100) + .boxed() + .flatMap(a -> IntStream.rangeClosed(a, 100) + .filter(b -> isPerfectSquare(a * a + b * b)) + .boxed() + .map(b -> new int[] {a, b, (int) Math.sqrt(a * a + b * b)}) + ); + + pythagoreanTriples.forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2])); + + } + + public static boolean isPerfectSquare(int n) { + return Math.sqrt(n) % 1 == 0; + } + +} diff --git a/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Reducing.java b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Reducing.java new file mode 100644 index 0000000..51245ca --- /dev/null +++ b/java8/src/main/java/com/brianway/learning/java8/streamapi/usage/Reducing.java @@ -0,0 +1,35 @@ +package com.brianway.learning.java8.streamapi.usage; + +import com.brianway.learning.java8.streamapi.Dish; +import static com.brianway.learning.java8.streamapi.Dish.menu; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * 归约 + */ +public class Reducing { + + public static void main(String... args) { + + List numbers = Arrays.asList(3, 4, 5, 1, 2); + int sum = numbers.stream().reduce(0, (a, b) -> a + b); + System.out.println(sum); + + int sum2 = numbers.stream().reduce(0, Integer::sum); + System.out.println(sum2); + + int max = numbers.stream().reduce(0, Integer::max); + System.out.println(max); + + Optional min = numbers.stream().reduce(Integer::min); + min.ifPresent(System.out::println); + + int calories = menu.stream() + .map(Dish::getCalories) + .reduce(0, Integer::sum); + System.out.println("Number of calories:" + calories); + } +} 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