[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"\u002Fresource\u002Fdocument\u002Flist?undefined":3,"\u002Fresource\u002Fdocument\u002Fquery\u002F5434a3cyyjvwhs8s?undefined":462,"\u002Fresource\u002Fadvertise\u002Flist?type=all?undefined":465},{"data":4,"status":460,"success":461},[5,148,202,291,332,370,420],{"books":6,"desc":145,"id":8,"image":146,"title":147},[7,40,63,78,93,105,117],{"cateId":8,"chapters":9,"desc":36,"id":11,"time":37,"title":38,"video":39},1,[10,15,18,21,24,27,30,33],{"bookId":11,"id":12,"indexOrder":13,"name":14},24,"8egfulw98v3h680j",0,"JavaSE 笔记（一）走进Java语言",{"bookId":11,"id":16,"indexOrder":13,"name":17},"pew6po6wrou23pk3","JavaSE 笔记（二）面向过程编程",{"bookId":11,"id":19,"indexOrder":13,"name":20},"eldst1fgrbdkmfs7","JavaSE 笔记（三）面向对象基础",{"bookId":11,"id":22,"indexOrder":13,"name":23},"48zphgkpjto8cath","JavaSE 笔记（四）面向对象高级篇",{"bookId":11,"id":25,"indexOrder":13,"name":26},"6r4llai92yc15j98","JavaSE 笔记（五）泛型程序设计",{"bookId":11,"id":28,"indexOrder":13,"name":29},"k6fmxd6qabgkwm9i","JavaSE 笔记（六）集合类与IO",{"bookId":11,"id":31,"indexOrder":13,"name":32},"qrd0xfttsz32gpqg","JavaSE 笔记（七）多线程与反射",{"bookId":11,"id":34,"indexOrder":13,"name":35},"td5tgn04nqmkrryt","JavaSE 笔记（八）GUI程序开发","基于Java25全新录制的SE课程",2025,"JavaSE 核心内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV163GGz2E8c",{"cateId":8,"chapters":41,"desc":59,"id":8,"time":60,"title":61,"video":62},[42,44,46,49,51,53,55,57],{"bookId":8,"id":43,"indexOrder":13,"name":14},"ibeeuwsbbi00undq",{"bookId":8,"id":45,"indexOrder":13,"name":17},"dncxjecdv4wciqcp",{"bookId":8,"id":47,"indexOrder":13,"name":48},"jviyz2hsht9ete5k","JavaSE 笔记（三）面向对象基础篇",{"bookId":8,"id":50,"indexOrder":13,"name":23},"qb9i6q9fap7bg1cc",{"bookId":8,"id":52,"indexOrder":13,"name":26},"hnkrjrkm3hjzeq6s",{"bookId":8,"id":54,"indexOrder":13,"name":29},"erpm32wduoaaqmrx",{"bookId":8,"id":56,"indexOrder":13,"name":32},"lfqtvxr7azumcwja",{"bookId":8,"id":58,"indexOrder":13,"name":35},"qs7gqok56gzc6idr","2022年制作的JavaSE版本",2022,"JavaSE 22年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1YP4y1o75f\u002F",{"cateId":8,"chapters":64,"desc":75,"id":66,"time":60,"title":76,"video":77},[65,69,72],{"bookId":66,"id":67,"indexOrder":13,"name":68},2,"g96k66kczovvbm1i","JVM 笔记（一）走进JVM",{"bookId":66,"id":70,"indexOrder":13,"name":71},"ydd7n3jg8unc3clg","JVM 笔记（二）内存管理",{"bookId":66,"id":73,"indexOrder":13,"name":74},"r9dq37de0kaeauoi","JVM 笔记（三）类与类加载","了解Java的底层运作机制","Java JVM 虚拟机","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Er4y1r7as\u002F",{"cateId":8,"chapters":79,"desc":90,"id":81,"time":60,"title":91,"video":92},[80,84,87],{"bookId":81,"id":82,"indexOrder":13,"name":83},3,"asncyye9ya18gfar","JUC 笔记（一）再谈多线程",{"bookId":81,"id":85,"indexOrder":13,"name":86},"5tr1sm4ho6ygpt9q","JUC 笔记（二）并发编程核心",{"bookId":81,"id":88,"indexOrder":13,"name":89},"1scf51z5300mzxkh","JUC 笔记（三）并发编程进阶","你也可以成为多线程的主宰者","Java JUC 并发编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1JT4y1S7K8\u002F",{"cateId":8,"chapters":94,"desc":102,"id":96,"time":60,"title":103,"video":104},[95,99],{"bookId":96,"id":97,"indexOrder":13,"name":98},4,"eedesc445ygiqhil","NIO 笔记（一）基础内容",{"bookId":96,"id":100,"indexOrder":13,"name":101},"ndz9t0uunrmfmv4n","NIO 笔记（二）Netty框架专题","编写畅快的高性能网络服务器","Java NIO 网络编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1ar4y1J7mC\u002F",{"cateId":8,"chapters":106,"desc":114,"id":108,"time":60,"title":115,"video":116},[107,111],{"bookId":108,"id":109,"indexOrder":13,"name":110},5,"9890i8ofuadpwy2b","[扩展篇] Java 9-17新特性介绍",{"bookId":108,"id":112,"indexOrder":13,"name":113},"tsrkqvb6zpmtwh0n","[扩展篇] JavaSE关键字总结 笔记","精彩仍在继续，不要停止脚步","其他内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1tU4y1y7Fg\u002F",{"cateId":8,"chapters":118,"desc":141,"id":120,"time":142,"title":143,"video":144},[119,123,126,129,132,135,138],{"bookId":120,"id":121,"indexOrder":13,"name":122},6,"4db9h32opv7imszh","JavaSE 笔记（一）面向过程编程",{"bookId":120,"id":124,"indexOrder":13,"name":125},"c93u3v37br7hgn1q","JavaSE 笔记（二）面向对象基础篇",{"bookId":120,"id":127,"indexOrder":13,"name":128},"yglsjde9gi1jxkcb","JavaSE 笔记（三）泛型与集合类",{"bookId":120,"id":130,"indexOrder":13,"name":131},"ilhi987n986rmvo3","JavaSE 笔记（四）异常机制",{"bookId":120,"id":133,"indexOrder":13,"name":134},"pqv38vexmenglk4k","JavaSE 笔记（五）IO",{"bookId":120,"id":136,"indexOrder":13,"name":137},"jiq41n87i9ia7ilw","JavaSE 笔记（六）多线程",{"bookId":120,"id":139,"indexOrder":13,"name":140},"wn7x2mge9ws79zps","JavaSE 笔记（七）反射","此版本为早期录制的旧版本",2021,"JavaSE 21年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Gv411T7pi\u002F","包含JavaSE基础路线全部教程笔记，打下坚实的基础","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-bf1a927f037a79f4d57d9ae543430a0d_1440w.webp","JavaSE 系列笔记 ☕️",{"books":149,"desc":199,"id":66,"image":200,"title":201},[150,166,178],{"cateId":66,"chapters":151,"desc":162,"id":153,"time":163,"title":164,"video":165},[152,156,159],{"bookId":153,"id":154,"indexOrder":13,"name":155},21,"iqbc2haub31bwqtz","Lombok 极速上手",{"bookId":153,"id":157,"indexOrder":13,"name":158},"ijay2hay19kn1k031","Mybatis 快速上手",{"bookId":153,"id":160,"indexOrder":13,"name":161},"ru4ogh2waocpn4jo","Maven 快速上手","JavaWeb阶段必须扩展知识点",2024,"常用知识讲解","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1gb421J7ok\u002F",{"cateId":66,"chapters":167,"desc":175,"id":169,"time":163,"title":176,"video":177},[168,172],{"bookId":169,"id":170,"indexOrder":13,"name":171},22,"ek20yvb6huhxizx7","JavaWeb 笔记（一）计算机网络基础",{"bookId":169,"id":173,"indexOrder":13,"name":174},"pgevws6w2krkffa4","JavaWeb笔记（二）Java与数据库","全面升级的JavaWeb课程","JavaWeb 网站开发","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1kS421X7rq\u002F",{"cateId":66,"chapters":179,"desc":196,"id":181,"time":142,"title":197,"video":198},[180,184,187,190,193],{"bookId":181,"id":182,"indexOrder":13,"name":183},7,"ggwwj09j2vkfftvd","JavaWeb 笔记（一）Java网络编程",{"bookId":181,"id":185,"indexOrder":13,"name":186},"sauvq105istskjaz","JavaWeb 笔记（二）数据库基础",{"bookId":181,"id":188,"indexOrder":13,"name":189},"xgbeasmvrhxx9tn4","JavaWeb 笔记（三）Java与数据库",{"bookId":181,"id":191,"indexOrder":13,"name":192},"k7dfwua3bsezvw9q","JavaWeb 笔记（四）前端基础",{"bookId":181,"id":194,"indexOrder":13,"name":195},"ycpagby2v7j4p728","JavaWeb 笔记（五）后端开发","搭建属于自己的Web网站","JavaWeb 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1CL4y1i7qR\u002F","包含JavaWeb路线全套笔记，从零开始搭建自己的网站！","https:\u002F\u002Fpic3.zhimg.com\u002F80\u002Fv2-df3b38e3012258ed70c23b586309e3f6_1440w.webp","JavaWeb 系列笔记 🚛",{"books":203,"desc":288,"id":81,"image":289,"title":290},[204,220,235,255,273],{"cateId":81,"chapters":205,"desc":216,"id":207,"time":217,"title":218,"video":219},[206,210,213],{"bookId":207,"id":208,"indexOrder":13,"name":209},8,"h7sjo5oy0l03607e","SSM笔记（一）Spring基础",{"bookId":207,"id":211,"indexOrder":13,"name":212},"eve8gq72qmdb46sg","SSM笔记（二）SpringMvc基础",{"bookId":207,"id":214,"indexOrder":13,"name":215},"63v73g0zh1qlr6fk","SSM笔记（三）SpringSecurity基础","Spring的探索之路从这里开始",2023,"JavaSSM 基础部分","[\"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Kv4y1x7is\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Lh4y1M7kx\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1fV411M7aS\u002F\"]",{"cateId":81,"chapters":221,"desc":232,"id":223,"time":217,"title":233,"video":234},[222,226,229],{"bookId":223,"id":224,"indexOrder":13,"name":225},16,"0k66v5r6slsfuog4","SpringBoot笔记（一）核心内容",{"bookId":223,"id":227,"indexOrder":13,"name":228},"bqlrnc2yvkaxo8s1","SpringBoot笔记（二）数据交互",{"bookId":223,"id":230,"indexOrder":13,"name":231},"wci9lb9tgea866jt","SpringBoot笔记（三）前后端分离","SpringBoot全新重制版","SpringBoot 新版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xu4y1m7UP\u002F",{"cateId":81,"chapters":236,"desc":252,"id":238,"time":60,"title":253,"video":254},[237,240,243,246,249],{"bookId":238,"id":239,"indexOrder":13,"name":225},9,"e43gl1ilygps032v",{"bookId":238,"id":241,"indexOrder":13,"name":242},"emnmd8nzfdb3hr50","SpringBoot笔记（二）Git版本控制",{"bookId":238,"id":244,"indexOrder":13,"name":245},"jjlolj5igvttvyhv","SpringBoot笔记（三）Redis数据库",{"bookId":238,"id":247,"indexOrder":13,"name":248},"skgr4ivb5curdoux","SpringBoot笔记（四）其他框架介绍",{"bookId":238,"id":250,"indexOrder":13,"name":251},"le91fqhu4dqui1k4","SpringBoot笔记（五）Linux系统","逐步走向企业级开发","SpringBoot 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1UL411V7f3\u002F",{"cateId":81,"chapters":256,"desc":270,"id":258,"time":60,"title":271,"video":272},[257,261,264,267],{"bookId":258,"id":259,"indexOrder":13,"name":260},10,"oejzo0l77zeb6a7e","SpringCloud笔记（一）微服务基础",{"bookId":258,"id":262,"indexOrder":13,"name":263},"f6eya9taaelsl35p","SpringCloud笔记（二）微服务进阶",{"bookId":258,"id":265,"indexOrder":13,"name":266},"35v1hbsfcdgagdnw","SpringCloud笔记（三）微服务应用",{"bookId":258,"id":268,"indexOrder":13,"name":269},"a782u84512tyuo1m","SpringCloud笔记（四）消息队列","体验微服务架构带来的魅力","SpringCloud 进阶","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1AL4y1j7RY\u002F",{"cateId":81,"chapters":274,"desc":285,"id":276,"time":142,"title":286,"video":287},[275,278,280,282],{"bookId":276,"id":277,"indexOrder":13,"name":209},11,"efjw75u8a251qxk5",{"bookId":276,"id":279,"indexOrder":13,"name":212},"guc134xb7sl78vju",{"bookId":276,"id":281,"indexOrder":13,"name":215},"u8ekxxucowr2b1tm",{"bookId":276,"id":283,"indexOrder":13,"name":284},"vkpmw9wbej21nei6","SSM笔记（四）MySQL进阶","此教程为2021年旧版教程","JavaSSM 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xL4y1H7Tq\u002F","包含Spring全套框架笔记，从开始到Spring Boot，以及众多运维小知识。","https:\u002F\u002Fpic4.zhimg.com\u002F80\u002Fv2-28c3144421220d7c048703281bc34f63_1440w.webp","Spring 系列笔记 🍏",{"books":292,"desc":329,"id":96,"image":330,"title":331},[293,308],{"cateId":96,"chapters":294,"desc":305,"id":296,"time":60,"title":306,"video":307},[295,299,302],{"bookId":296,"id":297,"indexOrder":13,"name":298},12,"jd3e8u5cmvx5gco6","C语言（一）计算机思维导论",{"bookId":296,"id":300,"indexOrder":13,"name":301},"lqv77apvx82nkkio","C语言（二）基础语法",{"bookId":296,"id":303,"indexOrder":13,"name":304},"xb0b9t37gyv96xns","C语言（三）高级特性","包含高等院校需要教授的全部内容","C语言程序设计","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Cr4y137os\u002F",{"cateId":96,"chapters":309,"desc":326,"id":311,"time":60,"title":327,"video":328},[310,314,317,320,323],{"bookId":311,"id":312,"indexOrder":13,"name":313},13,"8a046ps2e4w6k4py","数据结构与算法（一）线性结构篇",{"bookId":311,"id":315,"indexOrder":13,"name":316},"3ma8db91f9zrnkja","数据结构与算法（二）树形结构篇",{"bookId":311,"id":318,"indexOrder":13,"name":319},"0lsjm59k7cgu4tpr","数据结构与算法（三）散列表篇",{"bookId":311,"id":321,"indexOrder":13,"name":322},"0qzy7bogo0g2pusa","数据结构与算法（四）图结构篇",{"bookId":311,"id":324,"indexOrder":13,"name":325},"6gmcxcikcilyxblj","数据结构与算法（五）排序算法篇","虽然很难，但是它是考研必学科目","数据结构与算法","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV13W4y127Ey\u002F","你的内心一直有一个坚定的声音在告诉你，一定要考上一名研究生，向着未来前进吧！","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-ac128404efb29ce1c9d1ccc61024f1d1_1440w.webp","C语言 系列笔记 🥬",{"books":333,"desc":367,"id":108,"image":368,"title":369},[334,349,358],{"cateId":108,"chapters":335,"desc":346,"id":337,"time":163,"title":347,"video":348},[336,340,343],{"bookId":337,"id":338,"indexOrder":13,"name":339},17,"urw2e6gg1lprv65w","Kotlin（一）基础语法",{"bookId":337,"id":341,"indexOrder":13,"name":342},"t7lnl87f74f3v1ju","Kotlin（二）类与对象",{"bookId":337,"id":344,"indexOrder":13,"name":345},"v1zzvki0knb1xvml","Kotlin（三）高级特性","包含Kotlin语言完整基础部分","Kotlin程序设计基础","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1P94y1c7tV\u002F",{"cateId":108,"chapters":350,"desc":355,"id":352,"time":163,"title":356,"video":357},[351],{"bookId":352,"id":353,"indexOrder":13,"name":354},18,"ovbzpe7065bye1st","Kotlin扩展（一）","包含Kotlin额外扩展知识","Kotlin扩展篇","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Hg4y1m7Ca\u002F",{"cateId":108,"chapters":359,"desc":364,"id":361,"time":163,"title":365,"video":366},[360],{"bookId":361,"id":362,"indexOrder":13,"name":363},19,"3at7ybv04dmjc0wp","Gradle基础教程","Gradle配置教程（Kotlin）","Gradle教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Fc411x7xF\u002F","Kotlin让JVM平台焕发新的生机，让语言的表达更加优美","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-be815568f7c79c64cdaa171b0409786d_1440w.webp","Kotlin 系列笔记 ☘️",{"books":371,"desc":418,"id":120,"title":419},[372,391,403],{"cateId":120,"chapters":373,"desc":387,"id":375,"time":388,"title":389,"video":390},[374,378,381,384],{"bookId":375,"id":376,"indexOrder":13,"name":377},26,"zjf5qapwqtqiohcn","JavaScript笔记（一）基础语法",{"bookId":375,"id":379,"indexOrder":13,"name":380},"95jc6sjyjwcp9pvp","JavaScript笔记（二）核心知识",{"bookId":375,"id":382,"indexOrder":13,"name":383},"j35cdc1qz8dzq7pn","JavaScript笔记（三）进阶知识",{"bookId":375,"id":385,"indexOrder":13,"name":386},"sdhodlihphnpcg37","JavaScript笔记（四）前端基础","包含JavaScript最新语法规范讲解",2026,"JavaScript教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xq6gBgESU",{"cateId":120,"chapters":392,"desc":400,"id":394,"time":37,"title":401,"video":402},[393,397],{"bookId":394,"id":395,"indexOrder":13,"name":396},23,"bsisgazdftiz3o9c","HTML5笔记（一）基础内容",{"bookId":394,"id":398,"indexOrder":13,"name":399},"njol93fs34gfwuzf","HTML5笔记（二）高级内容","包含HTML基础内容和相关知识点","HTML5核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1BrBiYNEWg",{"cateId":120,"chapters":404,"desc":415,"id":406,"time":37,"title":416,"video":417},[405,409,412],{"bookId":406,"id":407,"indexOrder":13,"name":408},25,"jo74ciirtg8wh90y","CSS笔记（一）基础入门",{"bookId":406,"id":410,"indexOrder":13,"name":411},"ap5ixyomoejuw4ue","CSS笔记（二）盒模型和布局",{"bookId":406,"id":413,"indexOrder":13,"name":414},"4djgk5xy1lzpiuf2","CSS笔记（三）变换和过渡","包含CSS3基础内容和相关知识点","CSS3核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1sQeEzFEKi","包含Web前端学习路径全部教程笔记，打下坚实的基础","Web前端 系列笔记",{"books":421,"desc":458,"id":423,"image":368,"title":459},[422,432,450],{"cateId":423,"chapters":424,"desc":429,"id":426,"time":163,"title":430,"video":431},100,[425],{"bookId":426,"id":427,"indexOrder":13,"name":428},20,"o0ab271mkdsas87","Markdown基础语法","编写简洁而又优美的文档","Markdown教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1eJ4m157kC",{"cateId":423,"chapters":433,"desc":447,"id":435,"time":60,"title":448,"video":449},[434,438,441,444],{"bookId":435,"id":436,"indexOrder":13,"name":437},14,"6386mh7anqt4tzyv","设计模式（一）面向对象设计原则",{"bookId":435,"id":439,"indexOrder":13,"name":440},"8ftkb38wfn6ox0ug","设计模式（二）创建型",{"bookId":435,"id":442,"indexOrder":13,"name":443},"i1msql1k8y70etey","设计模式（三）结构型",{"bookId":435,"id":445,"indexOrder":13,"name":446},"5434a3cyyjvwhs8s","设计模式（四）行为型","使你的编码水平得到质的飞跃","设计模式系列","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1u3411P7Na\u002F",{"cateId":423,"chapters":451,"desc":456,"id":453,"time":60,"title":457},[452],{"bookId":453,"id":454,"indexOrder":13,"name":455},15,"zj9uvg0sp3b0sok8","Docker 容器技术 笔记","这里包含其他中间件课程笔记","其他中间件笔记","我们对知识的探索从未停止，只有不断地学习，才能走向美好的未来！","其他笔记分类 🌽",200,true,{"data":463,"status":460,"success":461},{"bookId":435,"content":464,"id":445,"indexOrder":96,"name":446},"![image-20230301112135747](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FCratwlYFIfVnH7q.png)\n\n# 设计模式（行为型）\n\n前面我们已经学习了12种设计模式，分为两类：\n\n* 创建型：关注对象创建\n* 结构型：关注类和对象的结构组织\n\n我们接着来看最后一种设计模式，也是最多的一种，行为型设计模式关注系统中对象之间的交互，研究系统在运行时对象之间的相互通信与协作，进一步明确对象的职责。\n\n## 解释器模式\n\n这种模式的使用场景较少，很少使用的一种设计模式，这里提一下就行。\n\n解释器顾名思义，就是对我们的语言进行解释，根据不同的语义来做不同的事情，比如我们在SE中学习的双栈计算器，正是根据我们输入的算式，去进行解析，并根据不同的运算符来不断进行计算。\n\n比如我们输入：1+2*3\n\n那么计算器就会进行解析然后根据语义优先计算2*3的结果然后在计算1+6最后得到7，详细实现请参考JavaSE篇双栈计算器实现。\n\n## 模板方法模式\n\n模板方法模式我们之前也见到过许多，我们先来看看什么是模板方法。\n\n有些时候，我们的业务可能需要经历很多个步骤来完成，比如我们生病了在医院看病，首先是去门诊挂号，然后等待叫号，然后是去找医生看病，确定病因后，就根据医生的处方去前台开药，最后付钱。这一整套流程看似是规规矩矩的，但是在这其中，某些步骤并不是确定的，比如医生看病这一步，由于不同的病因，可能会进行不同的处理，最后开出来的药方也会不同，所以，整套流程中，有些操作是固定的，有些操作可能需要根据具体情况而定。\n\n![image-20230301112153648](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FPjOsmFXyI2Cl3pU.png)\n\n在我们的程序中也是如此，可能某些操作是固定的，我们就可以直接在类中对应方法进行编写，但是可能某些操作需要视情况而定，由不同的子类实现来决定，这时，我们就需要让这些操作由子类来延迟实现了。现在我们就需要用到模板方法模式。\n\n我们先来写个例子：\n\n```java\n\u002F**\n * 抽象诊断方法，因为现在只知道挂号和看医生是固定模式，剩下的开处方和拿药都是不确定的\n *\u002F\npublic abstract class AbstractDiagnosis {\n\n    public void test(){\n        System.out.println(\"今天头好晕，不想起床，开摆，先跟公司请个假\");\n        System.out.println(\"去医院看病了~\");\n        System.out.println(\"1 >> 先挂号\");\n        System.out.println(\"2 >> 等待叫号\");\n        \u002F\u002F由于现在不知道该开什么处方，所以只能先定义一下行为，然后具体由子类实现\n      \t\u002F\u002F大致的流程先定义好就行\n        this.prescribe();\n        this.medicine();  \u002F\u002F开药同理\n    }\n\n    public abstract void prescribe();   \u002F\u002F开处方操作根据具体病症决定了\n\n    public abstract void medicine();   \u002F\u002F拿药也是根据具体的处方去拿\n}\n```\n\n现在我们定义好了抽象方法，只是将具体的流程先定义出来了，但是部分方法需要根据实现决定：\n\n```java\n\u002F**\n * 感冒相关的具体实现子类\n *\u002F\npublic class ColdDiagnosis extends AbstractDiagnosis{\n    @Override\n    public void prescribe() {\n        System.out.println(\"3 >> 一眼丁真，鉴定为假，你这不是感冒，纯粹是想摆烂\");\n    }\n\n    @Override\n    public void medicine() {\n        System.out.println(\"4 >> 开点头孢回去吃吧\");\n    }\n}\n```\n\n这样，我们就有了一个具体的实现类，并且由于看病的逻辑已经由父类定义好了，所以子类只需要实现需要实现的部分即可，这样我们就实现了简单的模板方法模式：\n\n```java\npublic static void main(String[] args) {\n    AbstractDiagnosis diagnosis = new ColdDiagnosis();\n    diagnosis.test();\n}\n```\n\n![image-20230301112206668](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FIVyz42i1brvUWfl.png)\n\n最后我们来看看在JUC中讲解AQS源码实现中出现的代码：\n\n```java\npublic final boolean release(int arg) {    \u002F\u002FAQS的锁释放操作\n    if (tryRelease(arg)) {   \u002F\u002F可以看到这里调用了tryRelease方法，但是此方法并不是在AQS实现的，而是不同的锁自行实现，因为AQS也不知道你这种类型的锁到底该怎么去解锁\n        Node h = head;\n        if (h != null && h.waitStatus != 0)\n            unparkSuccessor(h);\n        return true;\n    }\n    return false;\n}\n\nprotected boolean tryRelease(int arg) {\n    throw new UnsupportedOperationException();   \u002F\u002FAQS中不支持，需要延迟到具体的子类去实现\n}\n```\n\n模板方法模式，实际上部分功能的实现是在子类完成的：\n\n```java\nprotected final boolean tryRelease(int releases) {   \n  \u002F\u002FReentrantLock中的AQS Sync实现类，对tryRelease方法进行了具体实现\n    int c = getState() - releases;\n    if (Thread.currentThread() != getExclusiveOwnerThread())\n        throw new IllegalMonitorStateException();\n    boolean free = false;\n    if (c == 0) {\n        free = true;\n        setExclusiveOwnerThread(null);\n    }\n    setState(c);\n    return free;\n}\n```\n\n是不是现在感觉，这种层层套娃的写法，好像并不是这些大佬故意为了装逼才这样写的，而是真的在遵守规范编写，让代码更易懂一些，甚至你现在再回去推一遍会发现思路非常清晰。当然，除了这里之外，还有很多框架都使用了模板方法模式来设计类结构，还请各位小伙伴自行探索。\n\n## 责任链模式\n\n责任链模式也非常好理解，比如我们的钉钉审批，实际上就是一条流水线一样的操作，由你发起申请，然后经过多个部门主管审批，最后才能通过，所以你的申请表相当于是在一条责任链上传递。当然除了这样的直线型责任链之外，还有环形、树形等。\n\n![image-20230301112223069](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002Fl4C6SmDYKb2QeTi.png)\n\n实际上我们之前也遇到过很多种责任链，比如JavaWeb中学习的Filter过滤器，正是采用的责任链模式，通过将请求一级一级不断向下传递，来对我们所需要的请求进行过滤和处理。\n\n![image-20230301112235576](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FgO5IUmS17tQouAh.png)\n\n这里我们就使用责任链模式来模拟一个简单的面试过程，我们面试也是一面二面三面这样走的流程，这里我们先设计一下责任链上的各个处理器：\n\n```java\npublic abstract class Handler {\n\n    protected Handler successor;    \u002F\u002F这里我们就设计责任链以单链表形式存在，这里存放后继节点\n\n    public Handler connect(Handler successor){     \u002F\u002F拼接后续节点\n        this.successor = successor;\n        return successor;  \u002F\u002F这里返回后继节点，方便我们一会链式调用\n    }\n\n    public void handle(){\n        this.doHandle();   \u002F\u002F由不同的子类实现具体处理过程\n        Optional\n                .ofNullable(successor)\n                .ifPresent(Handler::handle);    \u002F\u002F责任链上如果还有后继节点，就继续向下传递\n    }\n\n    public abstract void doHandle();   \u002F\u002F结合上节课学习的模板方法，交给子类实现\n}\n```\n\n因为面试有很多轮，所以我们这里创建几个处理器的实现：\n\n```java\npublic class FirstHandler extends Handler{   \u002F\u002F用于一面的处理器\n    @Override\n    public void doHandle() {\n        System.out.println(\"============= 白马程序员一面 ==========\");\n        System.out.println(\"1. 谈谈你对static关键字的理解？\");\n        System.out.println(\"2. 内部类可以调用外部的数据吗？如果是静态的呢？\");\n        System.out.println(\"3. hashCode()方法是所有的类都有吗？默认返回的是什么呢？\");\n        System.out.println(\"以上问题会的，可以依次打在评论区\");\n    }\n}\n```\n\n```java\npublic class SecondHandler extends Handler{  \u002F\u002F二面\n    @Override\n    public void doHandle() {\n        System.out.println(\"============= 白马程序员二面 ==========\");\n        System.out.println(\"1. 如果我们自己创建一个java.lang包并且编写一个String类，能否实现覆盖JDK默认的？\");\n        System.out.println(\"2. HashMap的负载因子有什么作用？变化规律是什么？\");\n        System.out.println(\"3. 线程池的运作机制是什么？\");\n        System.out.println(\"4. ReentrantLock公平锁和非公平锁的区别是什么？\");\n        System.out.println(\"以上问题会的，可以依次打在评论区\");\n    }\n}\n```\n\n```java\npublic class ThirdHandler extends Handler{\n    @Override\n    public void doHandle() {\n        System.out.println(\"============= 白马程序员三面 ==========\");\n        System.out.println(\"1. synchronized关键字了解吗？如何使用？底层是如何实现的？\");\n        System.out.println(\"2. IO和NIO的区别在哪里？NIO三大核心组件？\");\n        System.out.println(\"3. TCP握手和挥手流程？少一次握手可以吗？为什么？\");\n        System.out.println(\"4. 操作系统中PCB是做什么的？运行机制是什么？\");\n        System.out.println(\"以上问题会的，可以依次打在评论区\");\n    }\n}\n```\n\n这样我们就编写好了每一轮的面试流程，现在我们就可以构建一个责任链了：\n\n```java\npublic static void main(String[] args) {\n    Handler handler = new FirstHandler();  \u002F\u002F一面首当其冲\n    handler\n            .connect(new SecondHandler())   \u002F\u002F继续连接二面和三面\n            .connect(new ThirdHandler());\n    handler.handle();   \u002F\u002F开始面试\n} \n```\n\n可以看到最后结果也是按照我们的责任链来进行的。\n\n## 命令模式\n\n大家有没有发现现在的家电都在趋向于智能化，通过一个中央控制器，我们就可以对家里的很多电器进行控制，比如国内做的比较好的小米智能家居系列，还有Apple的HomeKit等，我们只需要在一个终端上进行操作，就可以随便控制家里的电器。\n\n![image-20230301112308454](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FSuaYPFW4kojctM6.png)\n\n比如现在我们有很多的类，彩电、冰箱、空调、洗衣机、热水器等，既然现在我们要通过一个遥控器去控制他们，那么我们就需要将控制这些电器的指令都给设计好才行，并且还不能有太强的关联性。\n\n所有的电器肯定需要通过蓝牙或是红外线接受遥控器发送的请求，所以所有的电器都是接收者：\n\n```java\npublic interface Receiver {\n    void action();   \u002F\u002F具体行为，这里就写一个算了\n}\n```\n\n接着我们要控制这些电器，那么肯定需要一个指令才能控制：\n\n```java\npublic abstract class Command {   \u002F\u002F指令抽象，不同的电器有指令\n\n    private final Receiver receiver;\n\n    protected Command(Receiver receiver){   \u002F\u002F指定此命令对应的电器（接受者）\n        this.receiver = receiver;\n    }\n\n    public void execute() {\n        receiver.action();   \u002F\u002F执行命令，实际上就是让接收者开始干活\n    }\n}\n```\n\n最后我们来安排一个遥控器：\n\n```java\npublic class Controller {   \u002F\u002F遥控器只需要把我们的指令发出去就行了\n    public static void call(Command command){\n        command.execute();\n    }\n}\n```\n\n比如现在我们创建一个空调，那么它就是作为我们命令的接收者：\n\n```java\npublic class AirConditioner implements Receiver{\n    @Override\n    public void action() {\n        System.out.println(\"空调已开启，呼呼呼\");\n    }\n}\n```\n\n现在我们创建一个开启空调的命令：\n\n```java\npublic class OpenCommand extends Command {\n    public OpenCommand(AirConditioner airConditioner) {\n        super(airConditioner);\n    }\n}\n```\n\n最后我们只需要通过遥控器发送出去就可以了：\n\n```java\npublic static void main(String[] args) {\n    AirConditioner airConditioner = new AirConditioner();   \u002F\u002F先创建一个空调\n    Controller.call(new OpenCommand(airConditioner));   \u002F\u002F直接通过遥控器来发送空调开启命令\n}\n```\n\n通过这种方式，遥控器这个角色并不需要知道具体会执行什么，只需要发送命令即可，遥控器和电器的关联性就不再那么强了。\n\n## 迭代器模式\n\n迭代器我们在JavaSE篇就已经讲解过了，迭代器可以说是我们学习Java语言的基础，没有迭代器，集合类的遍历就成了问题，正是因为有迭代器的存在，我们才能更加优雅的使用foreach语法。\n\n回顾我们之前使用迭代器的场景：\n\n```java\npublic static void main(String[] args) {\n    List\u003CString> list = Arrays.asList(\"AAA\", \"BBB\", \"CCC\");\n    for (String s : list) {   \u002F\u002F使用foreach语法糖进行迭代，依次获取每一个元素\n        System.out.println(s);   \u002F\u002F打印一下\n    }\n}\n```\n\n编译之后的代码如下：\n\n```java\npublic static void main(String[] args) {\n    List\u003CString> list = Arrays.asList(\"AAA\", \"BBB\", \"CCC\");\n    Iterator var2 = list.iterator();   \u002F\u002F实际上这里本质是通过List生成的迭代器来遍历我们每个元素的\n\n    while(var2.hasNext()) {   \u002F\u002F判断是否还有元素可以迭代，没有就false\n        String s = (String)var2.next();   \u002F\u002F通过next方法得到下一个元素，每调用一次，迭代器会向后移动一位\n        System.out.println(s);    \u002F\u002F打印一下\n    }\n}\n```\n\n可以看到，当我们使用迭代器对List进行遍历时，实际上就像一个指向列表头部的指针，我们通过不断向后移动指针来依次获取所指向的元素：\n\n![image-20230301112320947](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FRjZSDFcPm4q7odM.png)\n\n![image-20230301112329551](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FB7P3OlIQz8wgTAR.png)\n\n这里，我们依照JDK提供的迭代器接口（JDK已经为我们定义好了一个迭代器的具体相关操作），也来设计一个迭代器：\n\n```java\npublic class ArrayCollection\u003CT> {    \u002F\u002F首先设计一个简单的数组集合，一会我们就迭代此集合内的元素\n\n    private final T[] array;   \u002F\u002F底层使用一个数组来存放数据\n\n    private ArrayCollection(T[] array){   \u002F\u002Fprivate掉，自己用\n        this.array = array;\n    }\n\n    public static \u003CT> ArrayCollection\u003CT> of(T[] array){   \u002F\u002F开个静态方法直接吧数组转换成ArrayCollection，其实和直接new一样，但是这样写好看一点\n        return new ArrayCollection\u003C>(array);\n    }\n}\n```\n\n现在我们就可以将数据存放在此集合中了：\n\n```java\npublic static void main(String[] args) {\n    String[] arr = new String[]{\"AAA\", \"BBB\", \"CCC\", \"DDD\"};\n    ArrayCollection\u003CString> collection = ArrayCollection.of(arr);\n}\n```\n\n接着我们就可以来实现迭代器接口了：\n\n```java\npublic class ArrayCollection\u003CT> implements Iterable\u003CT>{   \u002F\u002F实现Iterable接口表示此类是支持迭代的\n\n    ...\n\n    @Override\n    public Iterator\u003CT> iterator() {    \u002F\u002F需要实现iterator方法，此方法会返回一个迭代器，用于迭代我们集合中的元素\n        return new ArrayIterator();\n    }\n\n    public class ArrayIterator implements Iterator\u003CT> {   \u002F\u002F这里实现一个，注意别用静态，需要使用对象中存放的数组\n        private int cur = 0;   \u002F\u002F这里我们通过一个指针表示当前的迭代位置\n\n        @Override\n        public boolean hasNext() {     \u002F\u002F判断是否还有下一个元素\n            return cur \u003C array.length;   \u002F\u002F如果指针大于或等于数组最大长度，就不能再继续了\n        }\n\n        @Override\n        public T next() {   \u002F\u002F返回当前指针位置的元素并向后移动一位\n            return array[cur++];   \u002F\u002F正常返回对应位置的元素，并将指针自增\n        }\n    }\n}\n```\n\n接着，我们就可以对我们自己编写的一个简单集合类进行迭代了：\n\n```java\npublic static void main(String[] args) {\n    String[] arr = new String[]{\"AAA\", \"BBB\", \"CCC\", \"DDD\"};\n    ArrayCollection\u003CString> collection = ArrayCollection.of(arr);\n    for (String s : collection) {    \u002F\u002F可以直接使用foreach语法糖，当然最后还是会变成迭代器调用\n        System.out.println(s);\n    }\n}\n```\n\n最后编译出来的样子：\n\n```java\npublic static void main(String[] args) {\n    String[] arr = new String[]{\"AAA\", \"BBB\", \"CCC\", \"DDD\"};\n    ArrayCollection\u003CString> collection = ArrayCollection.of(arr);\n    Iterator var3 = collection.iterator();   \u002F\u002F首先获取迭代器，实际上就是调用我们实现的iterator方法\n\n    while(var3.hasNext()) {\n        String s = (String)var3.next();   \u002F\u002F直接使用next()方法不断向下获取\n        System.out.println(s);\n    }\n}\n```\n\n这样我们就实现了一个迭代器来遍历我们的元素。\n\n## 中介者模式\n\n在早期，我们想要和别人进行语音聊天，一般都是通过电话的方式，我们通过拨打他人的电话号码，来建立会话，不过这样有一个问题，比如我现在想要通知通知3个人某件事情，那么我就得依次给三个人打电话，甚至还会遇到一种情况，就是我们没有某个人的电话号码，但是其他人有，这时还需要告知这个人并进行转告，就很麻烦。\n\n![image-20230301112503997](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FUoChEBnujTDMGpF.png)\n\n但是现在我们有了Facetime、有了微信，我们可以同时让多个人参与到群通话中进行群聊，这样我们就不需要一个一个单独进行通话或是转达了。实际上正是依靠了一个中间商给我们提供了进行群体通话的平台，我们才能实现此功能，而这个平台实际上就是一个中间人。又比如我们想要去外面租房，但是我们怎么知道哪里有可以租的房子呢？于是我们就会上各大租房APP上去找房源，同样的，如果我们现在有房子需要出租，我们也不知道谁会想要租房子，同样的我们也会把房子挂在租房APP上展示，而当我们去租房时或是出租时，就会有一个称为中介的人来跟我们对接，实际上也是一种中介的模式。\n\n在我们的程序中，可能也会出现很多的对象，但是这些对象之间的相互调用关系错综复杂，可能一个对象要做什么事情就得联系好几个对象：\n\n![image-20230301112515239](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FPobq2ZB1dyExNs9.png)\n\n但是如果我们在这中间搞一个中间人：\n\n![image-20230301112526146](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002Ffru13VTnkDS2y4v.png)\n\n这样当我们要联系其他人时，一律找中介就可以了，中介存储了所有人的联系方式，这样就不会像上面一样乱成一团了。这里我们就以房产中介的例子来编写：\n\n```java\npublic class Mediator {   \u002F\u002F房产中介\n    private final Map\u003CString, User> userMap = new HashMap\u003C>();   \u002F\u002F在出售的房子需要存储一下\n\n    public void register(String address, User user){   \u002F\u002F出售房屋的人，需要告诉中介他的房屋在哪里\n        userMap.put(address, user);\n    }\n\n    public User find(String address){   \u002F\u002F通过此方法来看看有没有对应的房源\n        return userMap.get(address);\n    }\n}\n```\n\n接着就是用户了，用户有两种角色，一种是租房，一种是出租：\n\n```java\npublic class User {   \u002F\u002F用户可以是出售房屋的一方，也可以是寻找房屋的一方\n    String name;\n    String tel;\n\n    public User(String name, String tel) {\n        this.name = name;\n        this.tel = tel;\n    }\n  \n    public User find(String address, Mediator mediator){   \u002F\u002F找房子的话，需要一个中介和你具体想找的地方\n        return mediator.find(address);\n    }\n\n    @Override\n    public String toString() {\n        return name+\" (电话：\"+tel+\")\";\n    }\n}\n```\n\n现在我们来测试一下：\n\n```java\npublic static void main(String[] args) {\n    User user0 = new User(\"刘女士\", \"10086\");   \u002F\u002F出租人\n    User user1 = new User(\"李先生\", \"10010\");   \u002F\u002F找房人\n    Mediator mediator = new Mediator();   \u002F\u002F我是黑心中介\n\n    mediator.register(\"成都市武侯区天府五街白马程序员\", user0);   \u002F\u002F先把房子给中介挂上去\n\n    User user = user1.find(\"成都市武侯区天府五街下硅谷\", mediator);  \u002F\u002F开始找房子\n    if(user == null) System.out.println(\"没有找到对应的房源\");\n\n    user = user1.find(\"成都市武侯区天府五街白马程序员\", mediator);  \u002F\u002F开始找房子\n    System.out.println(user);   \u002F\u002F成功找到对应房源\n}\n```\n\n中介者模式优化了原有的复杂多对多关系，而是将其简化为一对多的关系，更容易理解一些。\n\n## 备忘录模式\n\n>  2021年10月1日下午，河南驻马店的一名13岁女中学生，因和同学发生不愉快喝下半瓶百草枯。\n>\n> 10月5日，抢救四天情况恶化，家属泣不成声称“肺部一个小时一变”。\n>\n> 10月6日下午，据武警河南省总队医院消息，“目前女孩仍在医院救治。”\n\n喝下百草枯，会给你后悔的时间，但是不会给你后悔的机会（百草枯含有剧毒物质，会直接导致肺部纤维化，这是不可逆的，一般死亡过程在一周左右，即使家里花了再多的钱，接受了再多的治疗，也无法逆转这一过程）相信如果再给这位小女孩一次机会，回到拿起百草枯的那一刻，一定不会再冲动地喝下了吧。\n\n![image-20230301112608808](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FPnkvjD7GORWNCaZ.png)\n\n备忘录模式，就为我们的软件提供了一个可回溯的时间节点，可能我们程序在运行过程中某一步出现了错误，这时我们就可以回到之前的某个被保存的节点上重新来过（就像艾克的大招），我们平时编辑文本的时候，当我们编辑出现错误时，就需要撤回，而我们只需要按下`Ctrl+Z`就可以回到上一步，这样就大大方便了我们的文本编辑。\n\n其实备忘录模式也可以应用到我们的程序中，如果你学习过安卓开发，安卓程序在很多情况下都会重新加载`Activity`，实际上安卓中`Activity`的`onSaveInstanceState`和`onRestoreInstanceState`就是用到了备忘录模式，分别用于保存和恢复，这样就算重新加载也可以恢复到之前的状态。\n\n这里我们就模拟一下对象的状态保存：\n\n```java\npublic class Student {\n    private String currentWork;   \u002F\u002F当前正在做的事情\n    private int percentage;   \u002F\u002F当前的工作完成百分比\n\n    public void work(String currentWork) {\n        this.currentWork = currentWork;\n        this.percentage = new Random().nextInt(100);\n    }\n\n    @Override\n    public String toString() {\n        return \"我现在正在做：\"+currentWork+\" (进度：\"+percentage+\"%)\";\n    }\n}\n```\n\n接着我们需要保存它在某一时刻的状态，我们来编写一个状态保存类：\n\n```java\npublic class State {\n    final String currentWork;\n    final int percentage;\n\n    State(String currentWork, int percentage) {   \u002F\u002F仅开放给同一个包下的Student类使用\n        this.currentWork = currentWork;\n        this.percentage = percentage;\n    }\n}\n```\n\n接着我们来将状态的保存和恢复操作都实现一下：\n\n```java\npublic class Student {\n    ...\n\n    public State save(){\n        return new State(currentWork, percentage);\n    }\n\n    public void restore(State state){\n        this.currentWork = state.currentWork;\n        this.percentage = state.percentage;\n    }\n\n    ...\n}\n```\n\n现在我们来测试一下吧：\n\n```java\npublic static void main(String[] args) {\n    Student student = new Student();\n    student.work(\"学Java\");   \u002F\u002F开始学Java\n    System.out.println(student);\n\n    State savedState = student.save();   \u002F\u002F保存一下当前的状态\n\n    student.work(\"打电动\");   \u002F\u002F刚打开B站播放视频，学一半开始摆烂了\n    System.out.println(student);\n\n    student.restore(savedState);   \u002F\u002F两级反转！回到上一个保存的状态\n    System.out.println(student);   \u002F\u002F回到学Java的状态\n}\n```\n\n可以看到，虽然在学习Java的过程中，中途摆烂了，但是我们可以时光倒流，回到还没开始摆烂的时候，继续学习Java：\n\n![image-20230301112626822](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FohGTRiEMYAay3zB.png)\n\n不过备忘录模式为了去保存对象的状态，会占用大量的资源，尤其是那种属性很多的对象，我们需要合理的使用才能保证程序稳定运行。\n\n## 观察者模式\n\n牵一发而动全身，一幅有序摆放的多米诺骨牌，在我们推到第一个骨牌时，后面的骨牌会不断地被上一个骨牌推倒：\n\n![image-20230301112702467](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FvZBkdeXEQrcTagV.png)\n\n在Java中，一个对象的状态发生改变，可能就会影响到其他的对象，与之相关的对象可能也会联动的进行改变。还有我们之前遇到过的监听器机制，当具体的事件触发时，我们在一开始创建的监听器就可以执行相关的逻辑。我们可以使用观察者模式来实现这样的功能，当对象发生改变时，观察者能够立即观察到并进行一些联动操作，我们先定义一个观察者接口：\n\n```java\npublic interface Observer {   \u002F\u002F观察者接口\n    void update();   \u002F\u002F当对象有更新时，会回调此方法\n}\n```\n\n接着我们来写一个支持观察者的实体类：\n\n```java\npublic class Subject {\n    private final Set\u003CObserver> observerSet = new HashSet\u003C>();\n\n    public void observe(Observer observer) {   \u002F\u002F添加观察者\n        observerSet.add(observer);\n    }\n\n    public void modify() {   \u002F\u002F模拟对象进行修改\n        observerSet.forEach(Observer::update);   \u002F\u002F当对象发生修改时，会通知所有的观察者，并进行方法回调\n    }\n}\n```\n\n接着我们就可以测试一下了：\n\n```java\npublic static void main(String[] args) {\n    Subject subject = new Subject();\n    subject.observe(() -> System.out.println(\"我是一号观察者！\"));\n    subject.observe(() -> System.out.println(\"我是二号观察者！\"));\n    subject.modify();\n}\n```\n\n这样，我们就简单实现了一下观察者模式，当然JDK也为我们提供了实现观察者模式相关的接口：\n\n```java\nimport java.util.Observable;    \u002F\u002Fjava.util包下提供的观察者抽象类\n\npublic class Subject extends Observable {   \u002F\u002F继承此抽象类表示支持观察者\n\n    public void modify(){\n        System.out.println(\"对对象进行修改！\");\n        this.setChanged();    \u002F\u002F当对对象修改后，需要setChanged来设定为已修改状态\n        this.notifyObservers(new Date());   \u002F\u002F使用notifyObservers方法来通知所有的观察者\n      \t\u002F\u002F注意只有已修改状态下通知观察者才会有效，并且可以给观察者传递参数，这里传递了一个时间对象\n    }\n}\n```\n\n我们来测试一下吧：\n\n```java\npublic static void main(String[] args) {\n    Subject subject = new Subject();\n    subject.addObserver((o, arg) -> System.out.println(\"监听到变化，并得到参数：\"+arg));  \n  \t\u002F\u002F注意这里的Observer是java.util包下提供的\n    subject.modify();   \u002F\u002F进行修改操作\n}\n```\n\n## 状态模式\n\n在标准大气压下，水在0度时会结冰变成固态，在0-100度之间时，会呈现液态，100度以上会变成气态，水这种物质在不同的温度下呈现出不同的状态，而我们的对象，可能也会像这样存在很多种状态，甚至在不同的状态下会有不同的行为，我们就可以通过状态模式来实现。\n\n![image-20230301112716993](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FgyAHTPpVQ2c5Y3E.png)\n\n我们来设计一个学生类，然后学生的学习方法会根据状态不同而发生改变，我们先设计一个状态枚举：\n\n```java\npublic enum State {   \u002F\u002F状态直接使用枚举定义\n    NORMAL, LAZY\n}\n```\n\n接着我们来编写一个学生类：\n\n```java\npublic class Student {\n\n    public class Student {\n\n    private State state;   \u002F\u002F使用一个成员来存储状态\n\n    public void setState(State state) {\n        this.state = state;\n    }\n\n    public void study(){  \n        switch (state) {   \u002F\u002F根据不同的状态，学习方法会有不同的结果\n            case LAZY:\n                System.out.println(\"只要我不努力，老板就别想过上想要的生活，开摆！\");\n                break;\n            case NORMAL:\n                System.out.println(\"拼搏百天，我要上清华大学！\");\n                break;\n        }\n    }\n}\n```\n\n我们来看看，在不同的状态下，是否学习会出现不同的效果：\n\n```java\npublic static void main(String[] args) {\n    Student student = new Student();\n    student.setState(State.NORMAL);   \u002F\u002F先正常模式\n    student.study();\n\n    student.setState(State.LAZY);   \u002F\u002F开启摆烂模式\n    student.study();\n}\n```\n\n状态模式更加强调当前的对象所处的状态，我们需要根据对象不同的状态决定其他的处理逻辑。\n\n## 策略模式\n\n对面卡兹克打野被开了，我们是去打小龙还是打大龙呢？这就要看我们团队这一局的打法策略了。\n\n![image-20230301112727567](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002F3WAha1ZeQUTHvCi.png)\n\n我们可以为对象设定一种策略，这样对象之后的行为就会按照我们在一开始指定的策略而决定了，看起来和前面的状态模式很像，但是，它与状态模式的区别在于，这种转换是“主动”的，是由我们去指定，而状态模式，可能是在运行过程中自动切换的。\n\n其实策略模式我们之前也遇到过，比如线程池的拒绝策略：\n\n```java\npublic static void main(String[] args) {\n    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 10,\n            TimeUnit.SECONDS, new SynchronousQueue\u003C>(),  \u002F\u002F这里不给排队\n            new ThreadPoolExecutor.AbortPolicy());   \u002F\u002F当线程池无法再继续创建新任务时，我们可以自由决定使用什么拒绝策略\n\n    Runnable runnable = () -> {\n        try {\n            TimeUnit.SECONDS.sleep(60);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n    };\n    \n    executor.execute(runnable);   \u002F\u002F连续提交两次任务，肯定塞不下，这时就得走拒绝了\n    executor.execute(runnable);\n}\n```\n\n可以看到，我们如果使用AbortPolicy，那么就是直接抛出异常：\n\n![image-20230301112805739](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002Fj3NdO1tzHfBDAn7.png)\n\n我们也可以使用其他的策略：\n\n```java\npublic static void main(String[] args) {\n    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 10,\n            TimeUnit.SECONDS, new SynchronousQueue\u003C>(),\n            new ThreadPoolExecutor.DiscardOldestPolicy());   \u002F\u002F使用DiscardOldestPolicy策略从队列中丢弃\n```\n\n这种策略就会从等待队列中踢出一个之前的，不过我们这里的等待队列是没有容量的那种，所以会直接炸掉：\n\n![image-20230301112814947](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002Fdi5A9acmTXOuv2V.png)\n\n至于具体原因，可以回去看看JUC篇视频教程。\n\n再比如我们现在有一个排序类，但是根据不同的策略，会使用不同的排序方案：\n\n```java\npublic interface Strategy {   \u002F\u002F策略接口，不同的策略实现也不同\n\n    Strategy SINGLE = Arrays::sort;   \u002F\u002F单线程排序方案\n    Strategy PARALLEL = Arrays::parallelSort;   \u002F\u002F并行排序方案\n    \n    void sort(int[] array);\n}\n```\n\n现在我们编写一个排序类：\n\n```java\npublic class Sorter {\n\n    private Strategy strategy;   \u002F\u002F策略\n\n    public void setStrategy(Strategy strategy) {\n        this.strategy = strategy;\n    }\n\n    public void sort(int[] array){\n        strategy.sort(array);\n    }\n}\n```\n\n现在我们就可以指定不同的策略进行排序了：\n\n```java\npublic static void main(String[] args) {\n    Sorter sorter = new Sorter();\n    sorter.setStrategy(Strategy.PARALLEL);    \u002F\u002F指定为并行排序方案\n    \n    sorter.sort(new int[]{9, 2, 4, 5, 1, 0, 3, 7});\n}\n```\n\n## 访问者模式\n\n公园中存在多个景点，也存在多个游客，不同的游客对同一个景点的评价可能不同；医院医生开的处方单中包含多种药元素，査看它的划价员和药房工作人员对它的处理方式也不同，划价员根据处方单上面的药品名和数量进行划价，药房工作人员根据处方单的内容进行抓药，相对于处方单来说，划价员和药房工作人员就是它的访问者，不过访问者的访问方式可能会不同。\n\n![image-20230301112827540](https:\u002F\u002Fs2.loli.net\u002F2023\u002F03\u002F01\u002FIws7kQE8Z6MCfNp.png)\n\n在我们的Java程序中，也可能会出现这种情况，我们就可以通过访问者模式来进行设计。\n\n比如我们日以继夜地努力，终于在某某比赛赢得了冠军，而不同的人对于这分荣誉，却有着不同的反应：\n\n```java\npublic class Prize {   \u002F\u002F奖\n    String name;   \u002F\u002F比赛名称\n    String level;    \u002F\u002F等级\n\n    public Prize(String name, String level) {\n        this.name = name;\n        this.level = level;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getLevel() {\n        return level;\n    }\n}\n```\n\n我们首先定义一个访问者接口：\n\n```java\npublic interface Visitor {\n    void visit(Prize prize);   \u002F\u002Fvisit方法来访问我们的奖项\n}\n```\n\n然后就是访问者相关的实现了：\n\n```java\npublic class Teacher implements Visitor {   \u002F\u002F指导老师作为一个访问者\n    @Override\n    public void visit(Prize prize) {   \u002F\u002F它只关心你得了什么奖以及是几等奖，这也关乎老师的荣誉\n        System.out.println(\"你得奖是什么奖？\"+prize.name);\n        System.out.println(\"你得了几等奖？\"+prize.level);\n    }\n}\n```\n\n```java\npublic class Boss implements Visitor{    \u002F\u002F你的公司老板作为一个访问者\n    @Override\n    public void visit(Prize prize) {   \u002F\u002F你的老板只关心这些能不能为公司带来什么效益，奖本身并不重要\n        System.out.println(\"你的奖项大么，能够为公司带来什么效益么？\");\n        System.out.println(\"还不如老老实实加班给我多干干，别去搞这些没用的\");\n    }\n}\n```\n\n```java\npublic class Classmate implements Visitor{   \u002F\u002F你的同学也可以作为一个访问者\n    @Override\n    public void visit(Prize prize) {   \u002F\u002F你的同学也关心你得了什么奖，不过是因为你是他的奖学金竞争对手，他其实并不希望你得奖\n        System.out.println(\"你得了\"+prize.name+\"奖啊，还可以\");\n        System.out.println(\"不过这个奖没什么含金量，下次别去了\");\n    }\n}\n```\n\n```java\npublic class Family implements Visitor{    \u002F\u002F你的家人也可以是一个访问者\n    @Override\n    public void visit(Prize prize) {   \u002F\u002F你的家人并不是最关心你得了什么奖，而是先关心你自己然后才是奖项，他们才是真正希望你好的人。这个世界很残酷，可能你会被欺负得遍体鳞伤，可能你会觉得活着如此艰难，但是你的背后至少还有爱你的人，为了他们，怎能就此驻足。\n        System.out.println(\"孩子，辛苦了，有没有好好照顾自己啊\");\n        System.out.println(\"你得了什么奖啊？\"+prize.name+\"，很不错，要继续加油啊！\");\n    }\n}\n```\n\n可以看到，这里我们就设计了四种访问者，但是不同的访问者对于某一件事务的处理可能会不同。访问者模式把数据结构和作用于结构上的操作解耦，使得操作集合可相对自由地演化，我们上面就是将奖项本身的属性和对于奖项的不同操作进行了分离。\n",{"data":466,"status":460,"success":461},[467,472],{"id":8,"image":468,"link":469,"name":470,"type":471},"\u002Fimage\u002Fadv\u002Frainyun-2025-06.webp","https:\u002F\u002Fwww.rainyun.com\u002Fitbaima_","雨云优惠购","cloud",{"id":66,"image":473,"link":474,"name":475,"type":476},"\u002Fimage\u002Fadv\u002Fsimcard-2025-11.webp","https:\u002F\u002Fmall.itbaima.cn","号卡优惠","simcard"]