java8学习:重构和调试
内容来自《 java8实战 》,本篇文章内容均为非盈利,旨为方便自己查询、总结备份、开源分享。如有侵权请告知,马上删除。
书籍购买地址:java8实战
- 这一篇主要讲使用Lambda表达式来重构之前的代码,并且应用到重用的设计模式之上,然后会介绍如何测试和使用Lambda表达式和StreamAPI
从匿名类到Lambda表达式的转换
-
下面是传统方式实现的匿名类
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("run"); } };
-
使用lambda重构上面的代码
Runnable runnable = () -> { System.out.println("run"); };
-
代码明显变少,但是需要注意的是匿名类和lambda中对this和super的定义是不一样的,在匿名类中,this代表的是匿名类本身,而在lambda中,他代表的包含类,并且匿名类可以定义与类变量重名的变量,而lambda不可以,如
int a = 2; Runnable runnable = new Runnable() { int a = 3; //正确 @Override public void run() { System.out.println(a); } }; Runnable runnable1 = () -> { int a = 3; //编译错误 System.out.println(a); };
-
在重载的时候,匿名类和lambda的表现也不一样,比如
@FunctionalInterface public interface People { void print(); } @Test public void test() throws IOException { doSome(new People() { @Override public void print() { System.out.println("people"); } }); doSome((People) () -> System.out.println("run")); } public static void doSome(People people){people.print();} public static void doSome(Runnable runnable){runnable.run();}
- 如上重载了doSome方法,当我们用匿名类来传递实现的时候一点问题都没有,但是在使用lambda的时候,由于people的方法和runnable方法都不需要参数,那么就会产生模糊的调用所以会导致编译出错,所以我们只能手动的去指定一个被调用的方法,原因是匿名类传递实现的时候其传递的类型在初始化的时候就能确定比如new people.而lambda只能是通过上下文去判断传入的参数到底是什么类型的,所以需要手动去指定
#lambda表达式到方法引用的转换
- 方法引用往往比实例点方法名更容易读,更能只管的表达代码意图,代码之前的文章中都有,并且这个概念比较容易理解,就不贴代码了
从命令时的数据处理切换到Stream
- Stream API帮助我们去遍历数据,只需要告诉他怎么遍历,而不需要写具体的遍历步骤,并且通过短路和延迟加载等可以对我们的操作进行优化
-
如下是一个命令式编程,过滤条件加收集数据到List
List<Dish> dishes = new ArrayList<>(); for (Dish dish:menu){ if (dish.getCalories() > 200){ dishes.add(dish); } }
-
使用Stream 应该这样
List<Dish> collect = menu.stream() .filter(dish -> dish.getCalories() > 200) .collect(Collectors.toList());
- 但是需要说的是:从复杂的命令式循环切换到Stream遍历是不容易的,因为涉及到break之类的控制语句,对于这些我在阅读本书的时候,书中给出的网址已经无法访问,所以对于复杂的控制流程目前自己还是使用命令式编程,如果以后在学习中知道了,会及时贴出来的
使用Lambda重构面向对象的设计模式
策略模式
- 自己理解的策略模式就是:根据不同的情形使用不同的实现
-
原来代码可能这样写
@FunctionalInterface public interface People { void doSome(String s); } public class Eat implements People { @Override public void doSome(String s) { System.out.println("s = " + s); System.out.println("Eat"); } } public class Drink implements People { @Override public void doSome(String s) { System.out.println("s = " + s); System.out.println("Drink"); } } public class MS { private final People people; public MS(People people) { this.people = people; } public void msDoSome(String s){ people.doSome(s); } } @Test public void test() { MS people1 = new MS(new Eat()); people1.msDoSome("eat..."); MS people2 = new MS(new Drink()); people2.msDoSome("drink..."); }
- 如上实现一个策略模式需要一个接口模板,然后分别去实现不同的策略,然后用一个"策略转换类"(自己瞎定义的,也是自己的理解)去实现不同的策略,但是这太长了,并且模板接口类已经是一个函数式接口,那么我们就无需这么麻烦了
@FunctionalInterface public interface People { void doSome(String s); } public class MS { private final People people; public MS(People people) { this.people = people; } public void msDoSome(String s){ people.doSome(s); } } @Test public void test() { MS ms = new MS((s) -> System.out.println("s = " + s)); ms.msDoSome("eat..."); }
- lambda比普通实现少了很多模板实现代码
模板方法
-
自己理解的模板方法就是:有固定部分,也有自实现部分,比如取钱,人人都需要到ATM面前,但是操作不一样,有的存钱有的取钱,如下
@ToString public class Customer { private int id; public Customer(int id) { this.id = id; } } abstract class Bank { public void process(int id){ System.out.println("欢迎"); //模板方法 Customer customer = BankInnerDatabase.getCustomerById(id); //模板方法 doSome(customer);//自定义步骤 } public abstract void doSome(Customer customer); static class BankInnerDatabase{ static Customer getCustomerById(int id){ return new Customer(id); } } } //如上是抽象类,如果我们要实现doSome就只能是继承然后实现抽象方法 public class MyDoSome extends Bank { @Override public void doSome(Customer customer) { System.out.println(customer + ":存钱"); } } //使用 public void test() { Bank bank = new MyDoSome(); bank.process(1); }
-
Lambda应该这样:由于抽象类中的抽象方法符合函数式接口Consumer,那么我们就可以用一个方法来代替实现类,比如
//修改Bank类 class Bank { public void process(int id, Consumer<Customer> consumer){ System.out.println("欢迎"); //模板方法 Customer customer = BankInnerDatabase.getCustomerById(id); //模板方法 consumer.accept(customer);//自定义步骤 } static class BankInnerDatabase{ static Customer getCustomerById(int id){ return new Customer(id); } } } //删除Bank实现类 //测试 public void test() { new Bank().process(1,(c)-> System.out.println( c + ":取钱")); }
观察者模式
-
自己理解的观察者模式:就是如果A发生变化,B就会观测到A的变化,然后针对变化做出反应
public interface Observer { void notify(String tweet); } class A implements Observer{ @Override public void notify(String mes) { System.out.println("A:"+mes); } } class B implements Observer{ @Override public void notify(String mes) { System.out.println("B:"+mes); } } class C implements Observer{ @Override public void notify(String mes) { System.out.println("C:"+mes); } } public interface MyActionListener { //注册服务 void registerServer(Observer observer); //通知方法 void notifyServer(String s); } class MyActionListenerImpl implements MyActionListener{ //注册服务列表 List<Observer> observers = new ArrayList<>(); @Override public void registerServer(Observer observer) { observers.add(observer); } @Override public void notifyServer(String s) { observers.forEach(observer -> observer.notify(s)); } } public class MyTest { @Test public void test() { MyActionListenerImpl im = new MyActionListenerImpl(); im.registerServer(new A()); im.registerServer(new C()); im.registerServer(new B()); //ABC服务都订阅了,如果通知那么ABC将都会受到通知 im.notifyServer("s"); } }
-
lambda实现
MyActionListenerImpl im = new MyActionListenerImpl(); im.registerServer((a)-> System.out.println(a)); im.registerServer((b)-> System.out.println(b)); im.registerServer((c)-> System.out.println(c)); im.notifyServer("tongzhi");
- 如上就不需要实现Observer接口了,但是并不代表什么时候lambda都可以用,在逻辑比较复杂的时候,应该还是首选类实现的方式
##责任链模式
- 自己理解的责任链模式:就是类似一个链条,但是其实实现就是指向而已,责任链的每一部分负责一个功能或者处理一个事情,然后达成最后的目标
-
将
LABDA IN JAVA 8 ACTION
字符串修正为lambda in java 8 action
public abstract class ObjectProcess<T> { private ObjectProcess<T> objectProcess; public T handle(T t){ T t1 = nextWork(t); if (null != objectProcess){ return objectProcess.handle(t1); } return t1; } abstract T nextWork(T t); public void setObjectProcess(ObjectProcess objectProcess) { this.objectProcess = objectProcess; } } public class LowerCaseProcess extends ObjectProcess<String> { @Override String nextWork(String s) { return s.toLowerCase(); } } public class ReplaceProcess extends ObjectProcess<String> { @Override String nextWork(String s) { return s.replace("labda","lambda"); } } //测试 public void test() { ObjectProcess<String> o1 = new LowerCaseProcess(); ObjectProcess<String> o2 = new ReplaceProcess(); o1.setObjectProcess(o2); String handle = o1.handle("LABDA IN JAVA 8 ACTION"); System.out.println("handle = " + handle); //handle = lambda in java 8 action }
- 如上就完成了对String字符串的处理,上面中有两部分,一是先将字母lower处理,然后在替换
-
我们用lambda替换如上的操作,如上代码是一种先..然后...关系,那么我们之前有一个函数就是andThen
UnaryOperator<String> lower = (s -> s.toLowerCase()); UnaryOperator<String> replace = (s -> s.replace("labda","lambda")); Function<String, String> then = lower.andThen(replace); String apply = then.apply("LABDA IN JAVA 8 ACTION"); System.out.println("apply = " + apply);//apply = lambda in java 8 action
- 如上我们就省去了很多不必要的代码,UnaryOperator类继承了Function,可以看做是一个Function的实现
工厂模式
-
自己理解的工厂模式:就是方便调用的static方法,并且隐藏了创建对象的方法细节
class Fruits{} class Banana extends Fruits{} //香蕉是黄色 class Watermelon extends Fruits{} //西瓜是绿色 public class MyFactory { public static Fruits createFruit(String color){ switch (color) { case "yellow" : return new Banana(); case "green" : return new Watermelon(); default: return new Fruits(); } } } //测试 public void test() { Fruits green = MyFactory.createFruit("green"); System.out.println(green.getClass());//class com.qidai.demotest.Watermelon }
-
使用lambda解决:之前的文章里提到了构造方法的引用,我们可以使用这种办法完成工厂模式的编写
class Fruits{} class Banana extends Fruits{} class Watermelon extends Fruits{} public class MyFactory { private static final Map<String, Supplier<Fruits>> map = new HashMap<>(); static { map.put("green",Watermelon::new); map.put("yellow",Banana::new); } public static Fruits createFruit(String color){ Supplier<Fruits> fruitsSupplier = map.get(color); if (fruitsSupplier != null){ return fruitsSupplier.get(); } return null; } } //测试方法是跟上面的代码是一样的
Lambda的调试
-
下面是一个错误代码
List<String> lists = Arrays.asList("S", null); lists.stream().map(String::toString).collect(Collectors.toList());
- 出现的错误是
java.lang.NullPointerException at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) //这行是个什么鬼 at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ....
- 如上的打注释的堆栈跟踪信息代表了lambda内部发生了错误,由于lambda没有名字,所以编译器只能为它指定一个名字,如果很多地方的lambda同时出错那么会崩溃的,但是在java8中事实就是这样..
-
需要注意的是:如果方法引用指向的是同一个类中声明的方法,那么他的名称是可以被打印的
public class MyTest { @Test public void test() { List<String> lists = Arrays.asList("S", null); lists.stream().map(MyTest::method).collect(Collectors.toList()); } public static String method(String s){ return s.toLowerCase(); } }
- 错误信息
java.lang.NullPointerException at com.qidai.demotest.MyTest.method(MyTest.java:17) //出现了 at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
- 对于lambda的自定义名称是没有办法的...
使用日志调试
-
peek:最初实际就是为了在流的每个操作之间插入一个动作,它只会将操作顺承到下一个操作,而不对流产生影响,他的定义如下
Stream<T> peek(Consumer<? super T> action);
-
实例
public void test() { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8); List<Integer> collect = integers.stream() .peek(integer -> System.out.println("from stream " + integer)) .map(integer -> integer + 1) .peek(integer -> System.out.println("from map " + integer)) .filter(integer -> integer %2 == 0) .peek(integer -> System.out.println("from filter " + integer)) .collect(Collectors.toList()); System.out.println(collect); }
- 结果
from stream 1 from map 2 from filter 2 from stream 2 from map 3 from stream 3 from map 4 from filter 4 from stream 4 from map 5 from stream 5 from map 6 from filter 6 from stream 6 from map 7 from stream 7 from map 8 from filter 8 from stream 8 from map 9 [2, 4, 6, 8]

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
c++基础(上) 听课流水账
1、pass by value / pass by pointer / pass by reference pass by value:实参和形参不是同一个值,因此交换的是形参的值,当函数swap结束后,a和b的值并没有发生交换 pass by pointer and pass by reference :实参和形参是相同的。 2、动态内存 申请失败的返回值是 0 而不是 NULL, null在cpp中废止了。 申请一个长度我10,初始值都为0的空间 int *num= new int[10](); 3、对比new和malloc 4、cpp内存模型 内存模型:栈、堆、静态区、常量区。 5、数组内存模型 5、const char * / char * const / char const * 在c中,const是只读变量,cpp里const指的是常量。 6、常量指针和指针常量 下图为p1常量指针——p1所指地址上的内容不可以通过p1做出改动 下图p2为指针常量——p2所指的地址不可以发生改动 数组名就是一个指针常量 实例: ...
- 下一篇
面试 Java 高级后端开发,要准备哪些知识点?
由于我做了比较长时间的技术面试官,根据我的面试体会,不少同学收到面试后,什么准备也不会做,到时候就来了。 这样做的后果是:不知彼,不知己,每战必殆。哪怕侥幸面试成,工资一定会被压得很低。 其实公司肯花时间让你去面试,前提条件一定是通过你的简历,一定发现了你和公司的匹配点,也就是说,一定是有录用意向的。 在技术面试的时间段里(最长1个小时),你如果能展现你的优势那是最好的,但如果你做不到这点,简单点,让面试官感觉你达到了最低标准即可。这好比在大学里考试,有些同学对某门课的知识点完全不懂,但也有可能通过考前突击和刷题来通过考试(不作弊),而且还有可能考高分。 至于通过技术面试后,项目经理或人事的面试一般是过滤特别差的,比如考察些团队协作能力和沟通表达能力等,这对大家来说应该不是问题。所以在本文里,就以Java后端高级开发为例,讲述下面试的准备点。方法是通用的,其它方向的同学也可以以此类推。 一、至少定出2天的准备时间 面试的准备时间一般别低于2天,如果可以,再延长些,但别太长。 比如是去现场面试,可以找的借口就比较多,比如项目忙,会多,时间间隔别超过5个工作日。比如周二收到通知,就尽量约到...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS关闭SELinux安全模块
- CentOS8安装Docker,最新的服务器搭配容器使用
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境
- 设置Eclipse缩进为4个空格,增强代码规范