DesignPattern系列文章已经跟大家聊了一大半,不过都是在讲一些常见的设计模式。接下来,我们主要讲一些不常用的设计模式。不常见的设计模式并不意味着你不需要理解它们,不需要知道它们。每一种设计模式都是为了处理一个业务场景或解决某个问题而存在的。换句话说,存在是合理的。学习设计模式的好处:提高查看框架源码的能力提高你的代码设计能力和复杂业务逻辑的编码能力为面试和后续的职业道路打下坚实的基础官方会问一些关于如何排错的问题OOM发生在一些在线机器上。现在更常见的是检查机器是否配置为自动生成转储文件。检查的方法一般是进入机器:/home/www/XXXXX(项目名称)/bin目录,会有一个setenv.sh文件cat,看文件是否配置:-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/tmp/heapdump.hprof第二种是将文件脱机转储到有问题的机器上。然后开始使用(MAT)等一些分析文件工具,查看频繁实例化的对象有问题,非常耗费资源,同时也会频繁触发GC操作,所以在处理一些封装了外部资源的对象时,应该合理的。避免这种情况。解决方案是重用和共享这些昂贵的对象。这就是对象池模式,也可以理解为池化技术。结构图如下:ResourcePool(资源池类):用于封装逻辑的类,即保存和管理资源列表Resource(资源类):用于封装具体的资源类,资源类被资源消耗池类,因此只要不重新分配资源池,就永远不会回收它们。客户端(请求客户端):设计模式的美妙之处在于上面使用资源类的结构定义。结构图其实可以理解为提前在机器内存中开辟一块内存,用来存放需要提前定义的对象。有需要的时候随时从内存中取一个,不用了就归还,实现一个池化技术。对象池模型示例假设朋友A现在想买车,但是他现在没有那么多钱,只能向同学B借钱,并承诺两个月后还钱。同学B的存款是固定的,假设这是资源池,那么朋友A就是请求客户端。那么代码的实现如下:publicclassMoney{//状态是被借出吗?privateIntegerstatus;//金额单位WprivateIntegermoney;publicIntegergetStatus(){returnstatus;}publicvoidsetStatus(Integerstatus){this.status=status;}publicIntegergetMoney(){returnmoney;}publicvoidsetMoney(Integermoney){this.money=money;}}定义了一个资源类(money),它有astatusflag当前对象是否被占用,这个是关键publicclassClassmateBPool{//对象池的大小可以理解为理想情况下,最多可以借2WpublicstaticintnumObjects=2;//最大的大小对象池可以理解为你所有的财产最多可以借到多少publicstaticintmaxObjects=5;//vector(Money)protectedVectorobjectsPool=null用于对象池中存储对象;publicClassmateBPool(){}/***初始化对象池*/publicsynchronizedvoidcreatePool(){//确保没有创建对象池。如果创建了,保存的对象objectsPool不会为空=newVector();for(inti=0;i3){thrownewRuntimeException("当前没有空闲金额");}}System.out.println("Borrowmoney"+JSON.toJSONString(money));//返回可用对象returnmoney;}/***返回金额*/publicvoidreturnMoney(Moneymoney){//确保对象池存在,如果对象不存在创建(不存在),直接返回nMoney=iterator.next();if(money==returnMoney){money.setStatus(0);System.out.println("还钱成功");break;}}}/***扩充资源池*/publicvoidcreateNewMoney(intincrement){for(inti=0;imaxObjects){return;}objectsPool.add(create());}}/***查询空闲amount*/privateMoneygetFreeMoney(){Iteratoriterator=objectsPool.iterator();while(iterator.hasNext()){Moneymoney=iterator.next();//判断是否被占用if(money.getStatus()==0){money.setStatus(1);returnmoney;}}returnnull;}publicMoneycreate(){Moneymoney=newMoney();money.setMoney(1);//0未被占用,1被占用money.setStatus(0);returnmoney;}}创建一个资源池类,其中包含createPool方法初始化资源池,getMoney方法获取空闲金额,returnMoney方法返回金额publicclassClientTest{publicstaticvoidmain(String[]args){ClassmateBPoolclassmateBPool=newClassmateBPool();//初始化连接池classmateBPool.createPool();//借钱Moneymoney=classmateBPool.getMoney();//还钱classmateBPool.returnMoney(money);//结果:对象池开始创建//借钱{"money":1,"status":1}//还钱成功//模拟10次请求for(inti=0;i<10;i++){Moneymoney1=classmateBPool.getMoney();}//result://对象池开始创建//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//重试次数:2//重试次数:3//重试次数:4//线程异常"main"java.lang.RuntimeException:Therecurrentlynoidleamount//模拟10次请求,同时第三次和第四次该还钱了for(inti=0;i<10;i++){Moneymoney1=classmateBPool.getMoney();if(i==3||i==4){classmateBPool.returnMoney(money1);}}//result://对象池开始创建//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//还款money成功//借钱{"money":1,"status":1}//还钱成功//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//借钱{"money":1,"status":1}//重试次数:2//重试次数:3//重试次数:4//Exceptioninthread"main"java.lang.RuntimeException:Therecurrentlynofreeamount}}最后是测试demo,使用1次请求,10次请求,返回的场景。以上是对象池模式定义和示例代码实现对于这种Pooling技术,在C3P0、DBCP、Proxool等连接池中比较常见,不过在common-pool包中也有一个常用的工具,有兴趣的同学可以通过这个demo再看源码比较他们是否理解对象池模式,对象池模式的优点:对象池中的对象可以被重新使用sed,减少对象创建、回收和内存消耗。缺点:需要额外的内存空间,内存大小和对象个数不好控制等。总结当前Java对象的分配操作不是很慢,这种业务基本没遇到过场景,所以我找不到适合你的业务代码示例。你可以把它理解为一种理解形式,方便我们看一些源码或者一些业务场景,提供一些思考。下面给大家分享第二种常见模式解释器模式解释器模式。不知道大家在写正则表达式的时候有没有想过一个问题。Java它如何解析我们写的表达式语法呢?不清楚的请看下面的解释器模式解释器模式定义:GOF中的定义:解释器模式定义了它的语法对于一种语言(或称为Grammar)的表示,并定义了一个解释器来处理这个语法。说白了,就是定义一个规则,用这个规则来解析按照这个规则写出来的句子。有点混乱?我们看一下结构图:Context(环境):用于封装全局信息,所有具体解释器访问ContextAbstraceExpression(抽象表达式):抽象接口类,声明了解释器的具体方法TerminalExpression(终端符号表达式):一个解释器类,实现与文法终结符相关的操作。NonTerminalExpression:实现语法的不同规则或符号的类。每个语法对应一个该类的结构图定义。设计模式的美妙之处来自于上面结构图的定义。比如看上面的结构图和定义。估计很多同学还不明白。让我们举一个更实际的例子。假设现在我们需要实现小学数学中的加减乘除运算,输入任意表达式得到准确的结果,如:(6?6)??3或2??3?6。当然,上面的例子不是直接输入就可以实现的。根据要求的文法规则,所以:(6?6)??3输入文法可以理解为66?3??2??3?6输入文法可以理解为23??6?with(6?6)??3是整个计算过程的例子,如图所示,废话不多说,我们直接开始编码吧。//抽象表达式的定义publicinterfaceExpression{Longinterpret();}//乘法表达式publicclassMultiplyExpressionimplementsExpression{Expressionleft;Expressionright;publicMultiplyExpression(Expressionleft,Expressionright){this.left=left;this.right=right;}@OverridepublicLonginterpret(){returnleft.interpret()*right.interpret();}}//数字表达式publicclassNumberExpressionimplementsExpression{privateLongnumber;publicNumberExpression(Longnumber){this.number=number;}@OverridepublicLonginterpret(){returnnumber;}}上面定义一个很简单abstractionExpression接口,声明了解释器的具体方法interpret,定义了不同的语法NonTerminalExpressionpublicclassContext{privateDequenumbers=newLinkedList<>();publicLongExpressionInterpreter(Stringexpression){Longresult=null;for(Stringex:expression.split("")){if(isOperator(ex)){Expressionexp=null;if(ex.equals("+")){exp=newAddExpression(numbers.pollFirst(),numbers.pollFirst());}if(ex.equals("*")){exp=newMultiplyExpression(numbers.pollFirst(),numbers.pollFirst());}//当有其他运算符时,在这里添加即可if(null!=exp){result=exp.interpret();numbers.addFirst(newNumberExpression(result));}}if(isNumber(ex)){numbers.addLast(newNumberExpression(Long.parseLong(ex)));}}returnresult;}//判断是否为运算符号。这里仅以?和??为例,更多可以展开numberprivatebooleanisNumber(Stringex){returnex.chars().allMatch(Character::isDigit);}}定义环境的具体操作规则。需要注意的是,如果后续操作符号比较多,后面只需要添加即可。我们使用Deque作为整个操作的序列表,也可以使用其他的方式比如Stack等等。publicclassExpressionPatternDemo{publicstaticvoidmain(String[]args){Contextcontext=newContext();Longresult=context.ExpressionInterpreter("66+3*");System.out.println(result);//result:36ContextcontextTwo=newContext();LongresultTwo=contextTwo.ExpressionInterpreter("23*6+");System.out.println(resultTwo);//result:12}}最后,让我们看看我们的测试演示。根据我们输入的语法规则,解释我们想要的结果,这就是解释器模式。因为我们很少接触解释器模式,所以大家可以把它当作一个人来理解,更多的是用在表达式或者规则引擎中。有兴趣的小伙伴可以看看Pattern.compile的源码。其实质就是使用解释器模式进行总结。对于业务代码中这些不常用的,或者说不常见的模式,我们只能和大家分享一下它的原理和应用场景,大家可以理解为一种理解形式,方便大家以后遇到的问题有个思路。多了解多了解对我们没有坏处。本次分享到此结束。我是敖丙你知道的越多,你不知道的就越多。下次见!