-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathdb.json
More file actions
1 lines (1 loc) · 327 KB
/
db.json
File metadata and controls
1 lines (1 loc) · 327 KB
1
{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"node_modules/hexo-theme-landscape/source/js/jquery-3.4.1.min.js","path":"js/jquery-3.4.1.min.js","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/js/script.js","path":"js/script.js","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/fancybox/jquery.fancybox.min.css","path":"fancybox/jquery.fancybox.min.css","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/fancybox/jquery.fancybox.min.js","path":"fancybox/jquery.fancybox.min.js","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/style.styl","path":"css/style.styl","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.eot","path":"css/fonts/fontawesome-webfont.eot","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/FontAwesome.otf","path":"css/fonts/FontAwesome.otf","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.ttf","path":"css/fonts/fontawesome-webfont.ttf","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.svg","path":"css/fonts/fontawesome-webfont.svg","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.woff","path":"css/fonts/fontawesome-webfont.woff","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.woff2","path":"css/fonts/fontawesome-webfont.woff2","modified":1,"renderable":1},{"_id":"node_modules/hexo-theme-landscape/source/css/images/banner.jpg","path":"css/images/banner.jpg","modified":1,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/style.styl","path":"css/style.styl","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/blank.gif","path":"fancybox/blank.gif","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_loading@2x.gif","path":"fancybox/fancybox_loading@2x.gif","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_overlay.png","path":"fancybox/fancybox_overlay.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_sprite.png","path":"fancybox/fancybox_sprite.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_loading.gif","path":"fancybox/fancybox_loading.gif","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_sprite@2x.png","path":"fancybox/fancybox_sprite@2x.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.css","path":"fancybox/jquery.fancybox.css","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.js","path":"fancybox/jquery.fancybox.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.pack.js","path":"fancybox/jquery.fancybox.pack.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/coderwall.png","path":"img/coderwall.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/delicious.png","path":"img/delicious.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/facebook.png","path":"img/facebook.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/douban.png","path":"img/douban.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/github.png","path":"img/github.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/img-err.png","path":"img/img-err.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/img-loading.png","path":"img/img-loading.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/google.png","path":"img/google.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/linkedin.png","path":"img/linkedin.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/pinboard.png","path":"img/pinboard.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/pinterest.png","path":"img/pinterest.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/rss.png","path":"img/rss.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/mail.png","path":"img/mail.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/scrollbar_arrow.png","path":"img/scrollbar_arrow.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/stackoverflow.png","path":"img/stackoverflow.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/weibo.png","path":"img/weibo.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/zhihu.png","path":"img/zhihu.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/img/twitter.png","path":"img/twitter.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/js/jquery.lazyload.js","path":"js/jquery.lazyload.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/js/instagram.js","path":"js/instagram.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/js/main.js","path":"js/main.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/js/mobile.js","path":"js/mobile.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/js/pc.js","path":"js/pc.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.eot","path":"css/fonts/fontawesome-webfont.eot","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.svg","path":"css/fonts/fontawesome-webfont.svg","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.svgz","path":"css/fonts/fontawesome-webfont.svgz","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.ttf","path":"css/fonts/fontawesome-webfont.ttf","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.woff","path":"css/fonts/fontawesome-webfont.woff","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/fancybox_buttons.png","path":"fancybox/helpers/fancybox_buttons.png","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-buttons.js","path":"fancybox/helpers/jquery.fancybox-buttons.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-buttons.css","path":"fancybox/helpers/jquery.fancybox-buttons.css","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-media.js","path":"fancybox/helpers/jquery.fancybox-media.js","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-thumbs.css","path":"fancybox/helpers/jquery.fancybox-thumbs.css","modified":0,"renderable":1},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-thumbs.js","path":"fancybox/helpers/jquery.fancybox-thumbs.js","modified":0,"renderable":1},{"_id":"source/assets/img/xiaofuge.jpeg","path":"assets/img/xiaofuge.jpeg","modified":0,"renderable":0}],"Cache":[{"_id":"source/_posts/hello-world.md","hash":"7d98d6592de80fdcd2949bd7401cec12afd98cdf","modified":1611386028468},{"_id":"node_modules/hexo-theme-landscape/LICENSE","hash":"c480fce396b23997ee23cc535518ffaaf7f458f8","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/_config.yml","hash":"b608c1f1322760dce9805285a602a95832730a2e","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/README.md","hash":"d2772ece6d4422ccdaa0359c3e07588834044052","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/package.json","hash":"538ffbfac0634ef4922991f4d9d78f195392f717","modified":1611386048971},{"_id":"node_modules/hexo-theme-landscape/languages/de.yml","hash":"3ebf0775abbee928c8d7bda943c191d166ded0d3","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/es.yml","hash":"76edb1171b86532ef12cfd15f5f2c1ac3949f061","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/fr.yml","hash":"415e1c580ced8e4ce20b3b0aeedc3610341c76fb","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/en.yml","hash":"3083f319b352d21d80fc5e20113ddf27889c9d11","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/hu.yml","hash":"284d557130bf54a74e7dcef9d42096130e4d9550","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/ja.yml","hash":"a73e1b9c80fd6e930e2628b393bfe3fb716a21a9","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/it.yml","hash":"89b7d91306b2c1a0f3ac023b657bf974f798a1e8","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/ko.yml","hash":"881d6a0a101706e0452af81c580218e0bfddd9cf","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/mn.yml","hash":"2e7523951072a9403ead3840ad823edd1084c116","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/nl.yml","hash":"12ed59faba1fc4e8cdd1d42ab55ef518dde8039c","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/no.yml","hash":"965a171e70347215ec726952e63f5b47930931ef","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/pt.yml","hash":"57d07b75d434fbfc33b0ddb543021cb5f53318a8","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/ru.yml","hash":"4fda301bbd8b39f2c714e2c934eccc4b27c0a2b0","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/tr.yml","hash":"a1cdbfa17682d7a971de8ab8588bf57c74224b5b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/zh-CN.yml","hash":"1efd95774f401c80193eac6ee3f1794bfe93dc5a","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/languages/zh-TW.yml","hash":"53ce3000c5f767759c7d2c4efcaa9049788599c3","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/category.ejs","hash":"765426a9c8236828dc34759e604cc2c52292835a","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/archive.ejs","hash":"2703b07cc8ac64ae46d1d263f4653013c7e1666b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/layout.ejs","hash":"0d1765036e4874500e68256fedb7470e96eeb6ee","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/index.ejs","hash":"aa1b4456907bdb43e629be3931547e2d29ac58c8","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/post.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/page.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/tag.ejs","hash":"eaa7b4ccb2ca7befb90142e4e68995fb1ea68b2e","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/scripts/fancybox.js","hash":"c857d7a5e4a5d71c743a009c5932bf84229db428","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/after-footer.ejs","hash":"414914ebb159fac1922b056b905e570ac7521925","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/archive-post.ejs","hash":"c7a71425a946d05414c069ec91811b5c09a92c47","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/article.ejs","hash":"dfd555c00e85ffc4207c88968d12b219c1f086ec","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/footer.ejs","hash":"3656eb692254346671abc03cb3ba1459829e0dce","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/archive.ejs","hash":"7cb70a7a54f8c7ae49b10d1f37c0a9b74eab8826","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/gauges-analytics.ejs","hash":"21a1e2a3907d1a3dad1cd0ab855fe6735f233c74","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/google-analytics.ejs","hash":"2ea7442ea1e1a8ab4e41e26c563f58413b59a3d0","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/head.ejs","hash":"f215d92a882247a7cc5ea80b241bedfcec0ea6ca","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/mobile-nav.ejs","hash":"e952a532dfc583930a666b9d4479c32d4a84b44e","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/header.ejs","hash":"c1acd247e14588cdf101a69460cb8319c18cd078","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/sidebar.ejs","hash":"930da35cc2d447a92e5ee8f835735e6fd2232469","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_widget/archive.ejs","hash":"beb4a86fcc82a9bdda9289b59db5a1988918bec3","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_widget/category.ejs","hash":"dd1e5af3c6af3f5d6c85dfd5ca1766faed6a0b05","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_widget/recent_posts.ejs","hash":"60c4b012dcc656438ff59997e60367e5a21ab746","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_widget/tag.ejs","hash":"2de380865df9ab5f577f7d3bcadf44261eb5faae","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_widget/tagcloud.ejs","hash":"b4a2079101643f63993dcdb32925c9b071763b46","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/js/script.js","hash":"998ed4c5b147e1299bf62beebf33514474f28112","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/fancybox/jquery.fancybox.min.css","hash":"1be9b79be02a1cfc5d96c4a5e0feb8f472babd95","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_extend.styl","hash":"222fbe6d222531d61c1ef0f868c90f747b1c2ced","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_variables.styl","hash":"581b0cbefdaa5f894922133989dd2d3bf71ded79","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/style.styl","hash":"9c451e5efd72c5bb8b56e8c2b94be731e99db05b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/date.ejs","hash":"f1458584b679545830b75bef2526e2f3eb931045","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/category.ejs","hash":"c6bcd0e04271ffca81da25bcff5adf3d46f02fc0","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/gallery.ejs","hash":"3d9d81a3c693ff2378ef06ddb6810254e509de5b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/tag.ejs","hash":"2fcb0bf9c8847a644167a27824c9bb19ac74dd14","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/nav.ejs","hash":"16a904de7bceccbb36b4267565f2215704db2880","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/layout/_partial/post/title.ejs","hash":"4d7e62574ddf46de9b41605fe3140d77b5ddb26d","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_util/mixin.styl","hash":"44f32767d9fd3c1c08a60d91f181ee53c8f0dbb3","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_util/grid.styl","hash":"0bf55ee5d09f193e249083602ac5fcdb1e571aed","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/archive.styl","hash":"db15f5677dc68f1730e82190bab69c24611ca292","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/article.styl","hash":"80759482d07063c091e940f964a1cf6693d3d406","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/comment.styl","hash":"79d280d8d203abb3bd933ca9b8e38c78ec684987","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/footer.styl","hash":"e35a060b8512031048919709a8e7b1ec0e40bc1b","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/header.styl","hash":"85ab11e082f4dd86dde72bed653d57ec5381f30c","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/highlight.styl","hash":"bf4e7be1968dad495b04e83c95eac14c4d0ad7c0","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/mobile.styl","hash":"a399cf9e1e1cec3e4269066e2948d7ae5854d745","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/sidebar-bottom.styl","hash":"8fd4f30d319542babfd31f087ddbac550f000a8a","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/sidebar.styl","hash":"404ec059dc674a48b9ab89cd83f258dec4dcb24d","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/_partial/sidebar-aside.styl","hash":"890349df5145abf46ce7712010c89237900b3713","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/js/jquery-3.4.1.min.js","hash":"88523924351bac0b5d560fe0c5781e2556e7693d","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/fancybox/jquery.fancybox.min.js","hash":"6181412e73966696d08e1e5b1243a572d0f22ba6","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.woff","hash":"28b782240b3e76db824e12c02754a9731a167527","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.woff2","hash":"d6f48cba7d076fb6f2fd6ba993a75b9dc1ecbf0c","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/FontAwesome.otf","hash":"048707bc52ac4b6563aaa383bfe8660a0ddc908c","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.eot","hash":"d980c2ce873dc43af460d4d572d441304499f400","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.ttf","hash":"13b1eab65a983c7a73bc7997c479d66943f7c6cb","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/images/banner.jpg","hash":"f44aa591089fcb3ec79770a1e102fd3289a7c6a6","modified":499162500000},{"_id":"node_modules/hexo-theme-landscape/source/css/fonts/fontawesome-webfont.svg","hash":"98a8aa5cf7d62c2eff5f07ede8d844b874ef06ed","modified":499162500000},{"_id":"public/2021/01/23/hello-world/index.html","hash":"900ce1805ac9d37017e2ab03439a0957f3ff5a99","modified":1611389794573},{"_id":"public/archives/index.html","hash":"dba93bbc6696d93b4c360289966839a3ee749341","modified":1611391502446},{"_id":"public/archives/2021/01/index.html","hash":"f60bec66978f0f2b620afe67a89d7a2cfb98bfa2","modified":1611391502446},{"_id":"public/archives/2021/index.html","hash":"95a578fc846457de9a2dfa82c025a047592f9441","modified":1611391502446},{"_id":"public/index.html","hash":"c2b34c76e1d8d065d22723529429dcbc2d247572","modified":1611391502446},{"_id":"public/css/fonts/fontawesome-webfont.woff","hash":"cafc4ac5761a0a252d33dce4ea3952cf9a38d832","modified":1611386581282},{"_id":"public/css/fonts/fontawesome-webfont.woff2","hash":"d6f48cba7d076fb6f2fd6ba993a75b9dc1ecbf0c","modified":1611386109579},{"_id":"public/css/fonts/FontAwesome.otf","hash":"048707bc52ac4b6563aaa383bfe8660a0ddc908c","modified":1611386109579},{"_id":"public/css/fonts/fontawesome-webfont.eot","hash":"3ce87b82c7a4ffdf65e96765c2ffda10b1a283c6","modified":1611386581282},{"_id":"public/css/fonts/fontawesome-webfont.ttf","hash":"1480b8101b02da9bc4c60341b5e185e63e585064","modified":1611386581282},{"_id":"public/fancybox/jquery.fancybox.min.css","hash":"1be9b79be02a1cfc5d96c4a5e0feb8f472babd95","modified":1611386109579},{"_id":"public/js/script.js","hash":"998ed4c5b147e1299bf62beebf33514474f28112","modified":1611386109579},{"_id":"public/css/style.css","hash":"4907c3d2fdfd2113a31cbcd1cf6a30517c2299b8","modified":1611386581282},{"_id":"public/css/images/banner.jpg","hash":"f44aa591089fcb3ec79770a1e102fd3289a7c6a6","modified":1611386109579},{"_id":"public/fancybox/jquery.fancybox.min.js","hash":"6181412e73966696d08e1e5b1243a572d0f22ba6","modified":1611386109579},{"_id":"public/js/jquery-3.4.1.min.js","hash":"88523924351bac0b5d560fe0c5781e2556e7693d","modified":1611386109579},{"_id":"public/css/fonts/fontawesome-webfont.svg","hash":"ba13657479b46daecb6336bfe376f84cef3ae58b","modified":1611386581282},{"_id":"themes/hexo-theme-yilia/layout/.DS_Store","hash":"693e513b44d5241ea897cc90cc71aeba091283b9","modified":1611386451690},{"_id":"themes/hexo-theme-yilia/_config.yml","hash":"880b527cfcc68b1ecdb0e553ac130ef15f0e551d","modified":1611391499159},{"_id":"themes/hexo-theme-yilia/layout/archive.ejs","hash":"2703b07cc8ac64ae46d1d263f4653013c7e1666b","modified":1611386451701},{"_id":"themes/hexo-theme-yilia/README.md","hash":"8648a81b3ae08a4accd6b0541533e662511e6400","modified":1611386451689},{"_id":"themes/hexo-theme-yilia/package.json","hash":"00357ef6f24eb049074da81809e98f973f528cca","modified":1611386451703},{"_id":"themes/hexo-theme-yilia/layout/page.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1611386451702},{"_id":"themes/hexo-theme-yilia/layout/index.ejs","hash":"aa1b4456907bdb43e629be3931547e2d29ac58c8","modified":1611386451701},{"_id":"themes/hexo-theme-yilia/layout/post.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1611386451702},{"_id":"themes/hexo-theme-yilia/layout/tag.ejs","hash":"eaa7b4ccb2ca7befb90142e4e68995fb1ea68b2e","modified":1611386451703},{"_id":"themes/hexo-theme-yilia/layout/_partial/.DS_Store","hash":"e32979f5a18bff84e197c13b48f4c54e03796e4a","modified":1611386451690},{"_id":"themes/hexo-theme-yilia/layout/layout.ejs","hash":"4a5566f704f3246f5ef77badddf18d2e16750328","modified":1611386451702},{"_id":"themes/hexo-theme-yilia/layout/_partial/after-footer.ejs","hash":"082504c9fd8600306d4ca55f03e3cbb0ddd99dd7","modified":1611386451691},{"_id":"themes/hexo-theme-yilia/layout/_partial/archive-post.ejs","hash":"8dfb6d68aa8a0661d26c32ce1ce1f45815364c3a","modified":1611386451691},{"_id":"themes/hexo-theme-yilia/layout/_partial/article.ejs","hash":"ef8dd558f908f293c34123c0b7ff879d2fd0c09f","modified":1611386451693},{"_id":"themes/hexo-theme-yilia/layout/_partial/google-analytics.ejs","hash":"f921e7f9223d7c95165e0f835f353b2938e40c45","modified":1611386451694},{"_id":"themes/hexo-theme-yilia/layout/_partial/footer.ejs","hash":"f2994e0acd1d606ebf4680afc4fa652e148ccf4e","modified":1611386451694},{"_id":"themes/hexo-theme-yilia/layout/category.ejs","hash":"765426a9c8236828dc34759e604cc2c52292835a","modified":1611386451701},{"_id":"themes/hexo-theme-yilia/layout/_partial/head.ejs","hash":"963c106412a3ab142871976978ecc9884aeca17a","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/layout/_partial/archive.ejs","hash":"a6e94061ac55b9eb55275f87b608d62f6ea35659","modified":1611386451692},{"_id":"themes/hexo-theme-yilia/layout/_partial/left-col.ejs","hash":"70a9951e4e2d30aabba88e75c3fa54b9235ce6a6","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/layout/_partial/mobile-nav.ejs","hash":"cd0af87ee781ac9c2b0e6a2a05b063d4bd497d9c","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/layout/_partial/header.ejs","hash":"6387a93dad7c3d778eb91e3821852fbf6813880c","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/source/css/.DS_Store","hash":"fc5a428fe40854a0ba66b4cea196c6700a57cae7","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/source/css/_extend.styl","hash":"8ab1ad313bd6707d248c5ca1ee9a5eab8d815e42","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/layout/_partial/mathjax.ejs","hash":"54fab4d3d64cb937d6baec7324ffabf9f202b883","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/source/css/style.styl","hash":"456e8cfe3b0b0371e81848ea9b0bc7ffd5360921","modified":1611386451710},{"_id":"themes/hexo-theme-yilia/source/css/_variables.styl","hash":"8b63ea3c7199524b9a1541075c6f8fb2c0d0ea3d","modified":1611386451707},{"_id":"themes/hexo-theme-yilia/source/fancybox/blank.gif","hash":"2daeaa8b5f19f0bc209d976c02bd6acb51b00b0a","modified":1611386451710},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_loading@2x.gif","hash":"273b123496a42ba45c3416adb027cd99745058b0","modified":1611386451711},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_overlay.png","hash":"b3a4ee645ba494f52840ef8412015ba0f465dbe0","modified":1611386451711},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_sprite.png","hash":"17df19f97628e77be09c352bf27425faea248251","modified":1611386451711},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_sprite@2x.png","hash":"30c58913f327e28f466a00f4c1ac8001b560aed8","modified":1611386451712},{"_id":"themes/hexo-theme-yilia/source/fancybox/fancybox_loading.gif","hash":"1a755fb2599f3a313cc6cfdb14df043f8c14a99c","modified":1611386451711},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.css","hash":"b6aa6692c2e5f8bd74d96827b78570f0c5683c20","modified":1611386451713},{"_id":"themes/hexo-theme-yilia/source/img/coderwall.png","hash":"fa84676c4d654e040e51fd34bfcd9f9348cd5331","modified":1611386451715},{"_id":"themes/hexo-theme-yilia/source/img/delicious.png","hash":"9553a5f5189e4a953e04a58a49dbfa74b86b73dd","modified":1611386451716},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.js","hash":"a82597493d75ea989ca586e09173cff332efe41e","modified":1611386451713},{"_id":"themes/hexo-theme-yilia/source/fancybox/jquery.fancybox.pack.js","hash":"9e0d51ca1dbe66f6c0c7aefd552dc8122e694a6e","modified":1611386451714},{"_id":"themes/hexo-theme-yilia/source/img/facebook.png","hash":"d19ad7a0903daf26817afd8753cd97e0cc714f54","modified":1611386451718},{"_id":"themes/hexo-theme-yilia/source/img/douban.png","hash":"e2ade003ffadd5826ee66ec23901c2d6e8607e4e","modified":1611386451717},{"_id":"themes/hexo-theme-yilia/source/img/github.png","hash":"b84d03b32fa388dcbf149296ebd16dce6223d48d","modified":1611386451719},{"_id":"themes/hexo-theme-yilia/source/img/img-err.png","hash":"23a63ea26eb3c1d5e677d9883cf36cc1a1a1228b","modified":1611386451720},{"_id":"themes/hexo-theme-yilia/source/img/img-loading.png","hash":"a9cd5cd11866824f31e3d1c5e23badfeb3f73031","modified":1611386451721},{"_id":"themes/hexo-theme-yilia/source/img/google.png","hash":"61a21fec7346fa3400b747ac9a201cf3d5bc013d","modified":1611386451720},{"_id":"themes/hexo-theme-yilia/source/img/linkedin.png","hash":"e203138fb53c257cb214e97f4e30091b9c568d2c","modified":1611386451721},{"_id":"themes/hexo-theme-yilia/source/img/pinboard.png","hash":"0891fbb6d092fa012bf936019923383d84c6aeb0","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/img/pinterest.png","hash":"9c72917f8779c083157c6ce7a5d62ed4874f0630","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/img/mail.png","hash":"fca8199cc77fdbd700a45bf56d091c82f4a67fe7","modified":1611386451721},{"_id":"themes/hexo-theme-yilia/source/img/rss.png","hash":"430fd47340e75214c081abd05cd7410cf7c71b86","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/img/scrollbar_arrow.png","hash":"d64a33c4ddfbdb89deeb6f4e3d36eb84dc4777c0","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/img/stackoverflow.png","hash":"da5dfe9043055c95e479d49c78cd3b020de608f2","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/js/.DS_Store","hash":"df2fbeb1400acda0909a32c1cf6bf492f1121e07","modified":1611386451723},{"_id":"themes/hexo-theme-yilia/source/img/weibo.png","hash":"280dae3fd38086158b4a1b57edb94c06b1a5014b","modified":1611386451723},{"_id":"themes/hexo-theme-yilia/source/img/zhihu.png","hash":"a6d6ef65e9ac82e613a311810391ebb90d9b1c1d","modified":1611386451723},{"_id":"themes/hexo-theme-yilia/source/img/twitter.png","hash":"14dbb8e62d056525253bc0de13acd1723da7a934","modified":1611386451722},{"_id":"themes/hexo-theme-yilia/source/js/jquery.lazyload.js","hash":"c11a2e7b330d16d06feabd0a8477099adf9d6799","modified":1611386451724},{"_id":"themes/hexo-theme-yilia/source/js/instagram.js","hash":"81e13cacf4947118ed1920e59b04ccf6beef6b86","modified":1611386451724},{"_id":"themes/hexo-theme-yilia/source/js/mobile.js","hash":"b68cc01d24e80973c48205f551da87f3f3427644","modified":1611386451724},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/date.ejs","hash":"c0c988334e857a77ba455a056dfa21809e7e76a5","modified":1611386451696},{"_id":"themes/hexo-theme-yilia/source/js/main.js","hash":"e2633f282e377a4169649c9f17dc96036ad4fc64","modified":1611386451724},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/nav.ejs","hash":"d19dee2082528e1844bed3aa4e4bd59f15fd7a7a","modified":1611386451697},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/category.ejs","hash":"d4f0e36f9a2167e91082dbd7d52425a06d2bebbf","modified":1611386451695},{"_id":"themes/hexo-theme-yilia/source/js/pc.js","hash":"a5397d34a04084ee089b4b1e26457ab46ecea63e","modified":1611386451724},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/duoshuo.ejs","hash":"e8399025ed3b980aedb821c92855889f5f12fd5b","modified":1611386451696},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/share.ejs","hash":"da39b4ba0c0ce4e1932fd45c5aee10e8aca41f28","modified":1611386451698},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/tag.ejs","hash":"78612cfc091d7d861a70455a0dc8c3036e460879","modified":1611386451699},{"_id":"themes/hexo-theme-yilia/layout/_partial/post/title.ejs","hash":"2f275739b6f1193c123646a5a31f37d48644c667","modified":1611386451700},{"_id":"themes/hexo-theme-yilia/source/css/_partial/archive.styl","hash":"8b349f1605024dcdae054e04f02d71a2e84957c2","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/source/css/_partial/article.styl","hash":"872fc4e63509fef885c939e5fd70e6ed439beced","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/source/css/_partial/footer.styl","hash":"7ca837a4cc34db1c35f01baec85eb10ccc64ea86","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/source/css/_partial/header.styl","hash":"67e59feb18eee6026717cb440d86ab9551782628","modified":1611386451704},{"_id":"themes/hexo-theme-yilia/source/css/_partial/highlight.styl","hash":"8cadf8437ce6f372802d3d28617a1ab97e7c818e","modified":1611386451705},{"_id":"themes/hexo-theme-yilia/source/css/_partial/instagram.styl","hash":"8a7b07bf5ea2d3588c0019f722c245bb1a8696af","modified":1611386451705},{"_id":"themes/hexo-theme-yilia/source/css/_partial/main.styl","hash":"4268f759920106a576c6037264076b36018ff73b","modified":1611386451705},{"_id":"themes/hexo-theme-yilia/source/css/_partial/mobile.styl","hash":"3a03b04ef8ac305aa5dbf7b9db99cd9377d07383","modified":1611386451705},{"_id":"themes/hexo-theme-yilia/source/css/_partial/mobile-slider.styl","hash":"e19c7fae6968ad3ea6cfc110900a991f9b5fce31","modified":1611386451705},{"_id":"themes/hexo-theme-yilia/source/css/_partial/page.styl","hash":"720b5b169bc28ccba3794efce9b7cd39f243dec7","modified":1611386451706},{"_id":"themes/hexo-theme-yilia/source/css/_partial/scroll.styl","hash":"5539a38f9acd603d453a0ea0d8ce10893cf83d22","modified":1611386451706},{"_id":"themes/hexo-theme-yilia/source/css/_partial/share.styl","hash":"22697b9a9877ab9f018364feb57aeea4a8313c9a","modified":1611386451706},{"_id":"themes/hexo-theme-yilia/source/css/_partial/wheelmenu.styl","hash":"74630c56944e27bef53ef0c0e391611a2eec2ed0","modified":1611386451707},{"_id":"themes/hexo-theme-yilia/source/css/_partial/tagcloud.styl","hash":"af0115de5c6455f899a2e09225b50224982c039d","modified":1611386451706},{"_id":"themes/hexo-theme-yilia/source/css/_util/mixin.styl","hash":"429bad87fc156eacf226c5e35b0eafc277f2504b","modified":1611386451707},{"_id":"themes/hexo-theme-yilia/source/css/_util/grid.styl","hash":"1aa883ab432d9e4139c89dcbd40ae2bd1528d029","modified":1611386451707},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.eot","hash":"3ce87b82c7a4ffdf65e96765c2ffda10b1a283c6","modified":1611386451708},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.svgz","hash":"4bfdd33ed702e32ae01399fcc2652377f78e7626","modified":1611386451709},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.ttf","hash":"1480b8101b02da9bc4c60341b5e185e63e585064","modified":1611386451710},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/fancybox_buttons.png","hash":"e385b139516c6813dcd64b8fc431c364ceafe5f3","modified":1611386451712},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.woff","hash":"cafc4ac5761a0a252d33dce4ea3952cf9a38d832","modified":1611386451710},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-buttons.css","hash":"1a9d8e5c22b371fcc69d4dbbb823d9c39f04c0c8","modified":1611386451712},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-buttons.js","hash":"dc3645529a4bf72983a39fa34c1eb9146e082019","modified":1611386451712},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-thumbs.js","hash":"47da1ae5401c24b5c17cc18e2730780f5c1a7a0c","modified":1611386451713},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-media.js","hash":"294420f9ff20f4e3584d212b0c262a00a96ecdb3","modified":1611386451712},{"_id":"themes/hexo-theme-yilia/source/fancybox/helpers/jquery.fancybox-thumbs.css","hash":"4ac329c16a5277592fc12a37cca3d72ca4ec292f","modified":1611386451713},{"_id":"themes/hexo-theme-yilia/source/css/fonts/fontawesome-webfont.svg","hash":"ba13657479b46daecb6336bfe376f84cef3ae58b","modified":1611386451709},{"_id":"public/fancybox/blank.gif","hash":"2daeaa8b5f19f0bc209d976c02bd6acb51b00b0a","modified":1611386581282},{"_id":"public/fancybox/fancybox_loading@2x.gif","hash":"273b123496a42ba45c3416adb027cd99745058b0","modified":1611386581282},{"_id":"public/fancybox/fancybox_overlay.png","hash":"b3a4ee645ba494f52840ef8412015ba0f465dbe0","modified":1611386581282},{"_id":"public/fancybox/fancybox_sprite.png","hash":"17df19f97628e77be09c352bf27425faea248251","modified":1611386581282},{"_id":"public/fancybox/fancybox_loading.gif","hash":"1a755fb2599f3a313cc6cfdb14df043f8c14a99c","modified":1611386581282},{"_id":"public/fancybox/fancybox_sprite@2x.png","hash":"30c58913f327e28f466a00f4c1ac8001b560aed8","modified":1611386581282},{"_id":"public/img/coderwall.png","hash":"fa84676c4d654e040e51fd34bfcd9f9348cd5331","modified":1611386581282},{"_id":"public/img/delicious.png","hash":"9553a5f5189e4a953e04a58a49dbfa74b86b73dd","modified":1611386581282},{"_id":"public/img/douban.png","hash":"e2ade003ffadd5826ee66ec23901c2d6e8607e4e","modified":1611386581282},{"_id":"public/img/facebook.png","hash":"d19ad7a0903daf26817afd8753cd97e0cc714f54","modified":1611386581282},{"_id":"public/img/github.png","hash":"b84d03b32fa388dcbf149296ebd16dce6223d48d","modified":1611386581282},{"_id":"public/img/img-err.png","hash":"23a63ea26eb3c1d5e677d9883cf36cc1a1a1228b","modified":1611386581282},{"_id":"public/img/img-loading.png","hash":"a9cd5cd11866824f31e3d1c5e23badfeb3f73031","modified":1611386581282},{"_id":"public/img/linkedin.png","hash":"e203138fb53c257cb214e97f4e30091b9c568d2c","modified":1611386581282},{"_id":"public/img/google.png","hash":"61a21fec7346fa3400b747ac9a201cf3d5bc013d","modified":1611386581282},{"_id":"public/img/pinboard.png","hash":"0891fbb6d092fa012bf936019923383d84c6aeb0","modified":1611386581282},{"_id":"public/img/pinterest.png","hash":"9c72917f8779c083157c6ce7a5d62ed4874f0630","modified":1611386581282},{"_id":"public/img/rss.png","hash":"430fd47340e75214c081abd05cd7410cf7c71b86","modified":1611386581282},{"_id":"public/img/scrollbar_arrow.png","hash":"d64a33c4ddfbdb89deeb6f4e3d36eb84dc4777c0","modified":1611386581282},{"_id":"public/img/mail.png","hash":"fca8199cc77fdbd700a45bf56d091c82f4a67fe7","modified":1611386581282},{"_id":"public/img/stackoverflow.png","hash":"da5dfe9043055c95e479d49c78cd3b020de608f2","modified":1611386581282},{"_id":"public/img/weibo.png","hash":"280dae3fd38086158b4a1b57edb94c06b1a5014b","modified":1611386581282},{"_id":"public/img/zhihu.png","hash":"a6d6ef65e9ac82e613a311810391ebb90d9b1c1d","modified":1611386581282},{"_id":"public/img/twitter.png","hash":"14dbb8e62d056525253bc0de13acd1723da7a934","modified":1611386581282},{"_id":"public/css/fonts/fontawesome-webfont.svgz","hash":"4bfdd33ed702e32ae01399fcc2652377f78e7626","modified":1611386581282},{"_id":"public/fancybox/helpers/fancybox_buttons.png","hash":"e385b139516c6813dcd64b8fc431c364ceafe5f3","modified":1611386581282},{"_id":"public/fancybox/jquery.fancybox.css","hash":"b6aa6692c2e5f8bd74d96827b78570f0c5683c20","modified":1611386581282},{"_id":"public/js/jquery.lazyload.js","hash":"c11a2e7b330d16d06feabd0a8477099adf9d6799","modified":1611386581282},{"_id":"public/js/instagram.js","hash":"f19adbcc0dac33536bc6660598059048ec901882","modified":1611386581282},{"_id":"public/js/main.js","hash":"0640b68a76fab3c693b3cd1e4d04d14be1e53940","modified":1611386581282},{"_id":"public/js/mobile.js","hash":"b68cc01d24e80973c48205f551da87f3f3427644","modified":1611386581282},{"_id":"public/js/pc.js","hash":"fdbc039fc9ffa70815b5fc4daaa587ae29693f10","modified":1611386581282},{"_id":"public/fancybox/helpers/jquery.fancybox-buttons.css","hash":"1a9d8e5c22b371fcc69d4dbbb823d9c39f04c0c8","modified":1611386581282},{"_id":"public/fancybox/helpers/jquery.fancybox-buttons.js","hash":"dc3645529a4bf72983a39fa34c1eb9146e082019","modified":1611386581282},{"_id":"public/fancybox/helpers/jquery.fancybox-media.js","hash":"294420f9ff20f4e3584d212b0c262a00a96ecdb3","modified":1611386581282},{"_id":"public/fancybox/helpers/jquery.fancybox-thumbs.js","hash":"47da1ae5401c24b5c17cc18e2730780f5c1a7a0c","modified":1611386581282},{"_id":"public/fancybox/helpers/jquery.fancybox-thumbs.css","hash":"4ac329c16a5277592fc12a37cca3d72ca4ec292f","modified":1611386581282},{"_id":"public/fancybox/jquery.fancybox.pack.js","hash":"9e0d51ca1dbe66f6c0c7aefd552dc8122e694a6e","modified":1611386581282},{"_id":"public/fancybox/jquery.fancybox.js","hash":"a82597493d75ea989ca586e09173cff332efe41e","modified":1611386581282},{"_id":"source/assets/img/xiaofuge.jpeg","hash":"3b833fa4cf4f549e5bce9ff9277cb72dcca2115c","modified":1609050536651},{"_id":"public/assets/img/xiaofuge.jpeg","hash":"3b833fa4cf4f549e5bce9ff9277cb72dcca2115c","modified":1611387343404},{"_id":"source/_posts/coffee-pc.markdown","hash":"f80b1a449320e2209993400098d6440ef3414ee3","modified":1501267868000},{"_id":"public/archives/2012/index.html","hash":"269d3000c6d7754ab6396833675ef4159521012f","modified":1611389112530},{"_id":"public/archives/2012/12/index.html","hash":"eff1d6f2ac79ac130a385a95e1941756edb298e3","modified":1611389112530},{"_id":"public/tags/js/index.html","hash":"ec50b143e413bdd49f7d800e21dafecdc1be779a","modified":1611389112530},{"_id":"public/tags/coffeescript/index.html","hash":"47503049f88b1dc1a980033bbbad697796be043a","modified":1611389112530},{"_id":"public/2012/12/24/coffee-pc/index.html","hash":"16b2c2c4187717feb2276385671094eb6825188b","modified":1611389112530},{"_id":"source/_posts/数学,离一个程序员有多近?.md","hash":"edbc5a2d91f3cb9e058d089296be4e538cfd7171","modified":1611390914307},{"_id":"public/tags/java/index.html","hash":"6a57856acdcb9745a46646f070b4b2e498a8179f","modified":1611391502446},{"_id":"public/tags/数据结构/index.html","hash":"f57764bfbd9871e0d7c8e3d14a86a7e8c62b2ce6","modified":1611391502446},{"_id":"public/tags/算法逻辑/index.html","hash":"35bf5e50e15560f314a22f548e259ec9423e2831","modified":1611391502446},{"_id":"public/2021/01/17/数学,离一个程序员有多近?/index.html","hash":"959eb252b3be54786bca563e59382100eaa966a1","modified":1611391502446},{"_id":"source/_posts/握草,你竟然在代码里下毒!.md","hash":"dc86ff2251129bf3c2fe35e897525ba3bd584d05","modified":1611390900244},{"_id":"public/archives/2020/index.html","hash":"77b1c528b1a3c21b560cd60b281b3bafe0553a44","modified":1611391502446},{"_id":"public/archives/2020/09/index.html","hash":"b1f92f08069d399c9779af36fe444fc13d456e49","modified":1611391502446},{"_id":"public/tags/屎山代码/index.html","hash":"891eaaa150c97033b39e04039f39e3ca9b5b3a96","modified":1611391502446},{"_id":"public/2020/09/06/握草,你竟然在代码里下毒!/index.html","hash":"fc5ec2b833fd26d9766f36b42d072616219eb580","modified":1611391502446},{"_id":"source/_posts/重学 Java 设计模式.md","hash":"aa7bb15cbf0f272ac612950f537ba476c7988621","modified":1611391230227},{"_id":"source/_posts/一次代码评审,差点过不了试用期!.md","hash":"03079578a1bf01f7ddfa6d78349f879141c1f9f4","modified":1611391141648},{"_id":"public/tags/代码评审/index.html","hash":"22ba4a9cc9c5bad0923192340620ed8cbcfaa520","modified":1611391502446},{"_id":"public/tags/设计优化/index.html","hash":"6e4f8c51c0b6da01a4f7996bb916f9e464382ba5","modified":1611391502446},{"_id":"public/tags/设计模式/index.html","hash":"3fd7c374f725eaffe2097f44e8a003ce76a55895","modified":1611391502446},{"_id":"public/2021/01/23/重学 Java 设计模式/index.html","hash":"c083cf917cfc0fccedaad52f6c45c1181cdd16a0","modified":1611391502446},{"_id":"public/2020/09/14/一次代码评审,差点过不了试用期!/index.html","hash":"312be6f436b74deac877aa29a154c4736e3f2191","modified":1611391502446}],"Category":[],"Data":[],"Page":[],"Post":[{"layout":"post","title":"数学,离一个程序员有多近?","date":"2021-01-17T13:20:00.000Z","comments":1,"_content":"\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`数学离程序员有多近?`\n\nifelse也好、for循环也罢,代码可以说就是对**数学逻辑的具体实现**。所以敲代码的程序员几乎就离不开数学,难易不同而已。\n\n那数学不好就写不了代码吗😳?不,一样可以写代码,可以写出更多的`CRUD`出来。那你不要总觉得是产品需求简单所以你的实现过程才变成了增删改查,往往也是因为你还不具备可扩展、易维护、高性能的代码实现方案落地能力,才使得你小小年纪写出了更多的`CRUD`!\n\n<!-- more -->\n\n与一锥子买卖的小作坊相比,大厂和超级大厂更会注重数学能力。\n\n\n\n**2004年**,在硅谷的交通动脉 101 公路上突然出现一块巨大的广告牌,上面是一道数学题:` {e 的连续数字中最先出现的 10 位质数}`.com。\n\n广告:这里的 e 是数学常数,自然对数的底数,无限不循环小数。这道题的意思就是,找出 e 中最先出现的 10 位质数,然后可以得出一个网址。进入这个网址会看到 Google 为你出的第二道数学题,成功解锁这步 Google 会告诉你,`我们或许是”志同道合“的人`,你可以将简历发到这个邮箱,我们一起做点改变世界的事情。\n\n*计算 e 值可以通过泰勒公式推导出来:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推导计算过程还包括`埃拉托色尼筛选法(the Sieve of Eratosthenes)`、`线性筛选法`的使用。感兴趣的小伙伴可以用代码实现下。*\n\n## 二、把代码写好的四步\n\n`业务提需求、产品定方案、研发做实现。`最终这个系统开发的怎么样是由三方共同决定的!\n\n- 地基挖的不好,楼就盖不高\n- 砖头摆放不巧,楼就容易倒\n- 水电走线不妙,楼就危险了\n- 格局设计不行,楼就卖不掉\n\n这里的地基、砖头、水电、格局,对应的就是,数据结构、算法逻辑、设计模式、系统架构。从下到上相互依赖、相互配合,只有这一层做好,下一层才好做!\n\n\n\n- **数据结构**:高矮胖瘦、长宽扁细,数据的存放方式,是一套程序开发的核心基础。不合理的设计往往是从数据结构开始,哪怕你仅仅是使用数据库存放业务信息,也一样会影响到将来各类数据的查询、汇总等实现逻辑的难易。\n- **算法逻辑**:是对数据结构的使用,合适的数据结构会让算法实现过程降低时间复杂度。可能你现在的多层for循环在合适的算法过程下,能被优化为更简单的方式获取数据。*注意:算法逻辑实现,并不一定就是排序、归并,还有你实际业务的处理流程。*\n- **设计模式**:可以这么说,不使用设计模式你一样能写代码。但你愿意看到满屏幕的ifelse判断调用,还是喜欢像膏药一样的代码,粘贴来复制去?那么设计模式这套通用场景的解决方案,就是为你剔除掉代码实现过程中的恶心部分,让整套程序更加易维护、易扩展。*就是开发完一个月,你看它你还认识!*\n- **系统架构**:描述的是三层MVC,还是四层DDD。我对这个的理解就是家里的三居还是四局格局,MVC是我们经常用的大家都熟悉,DDD无非就是家里多了个书房,把各自属于哪一个屋子的摆件规整到各自屋子里。*那么乱放是什么效果呢,就是自动洗屁屁马桶🚽给按到厨房了,再贵也格楞子!* 好,那么我们在延展下,如果你的卫生间没有流出下水道咋办?是不这个地方的数据结构就是设计缺失的,而到后面再想扩展就难了吧!\n\n所以,研发在承接业务需求、实现产品方案的时候。压根就不只是在一个房子的三居或者四居格局里,开始随意码砖。\n\n没有合理的数据结构、没有优化的算法逻辑、没有运用的设计模式,最终都会影响到整个系统架构变得臃肿不堪,调用混乱。在以后附加、迭代、新增的需求下,会让整个系统问题不断的放大,当你想用重构时,就有着千丝万缕般调用关系。 *重构就不如重写了!*\n\n## 三、for循环没算法快\n\n在《编程之美》一书中,有这样一道题。求:1~n中,1出现的次数。比如:1~10,1出现了两次。\n\n### 1. for 循环实现\n\n```java\nlong startTime = System.currentTimeMillis();\nint count = 0;\nfor (int i = 1; i <= 10000000; i++) {\n String str = String.valueOf(i);\n for (int j = 0; j < str.length(); j++) {\n if (str.charAt(j) == 49) {\n count++;\n }\n }\n}\nSystem.out.println(\"1的个数:\" + count);\nSystem.out.println(\"计算耗时:\" + (System.currentTimeMillis() - startTime) + \"毫秒\");\n```\n\n使用 for 循环的实现过程很好理解,就是往死了循环。之后把循环到的数字按照字符串拆解,判断每一位是不是数字,是就+1。这个过程很简单,但是时间复杂很高。\n\n### 2. 算法逻辑实现\n\n\n\n如图 20-3 所示,其实我们能发现这个1的个数在100、1000、10000中是有规则的循环出现的。11、12、13、14或者21、31、41、51,以及单个的1出现。最终可以得出通用公式:`abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...`,abcd代表位数。另外在实现的过程还需要考虑比如不足100等情况,例如98、1232等。\n\n**实现过程**\n\n```java\nlong startTime = System.currentTimeMillis();\nint num = 10000000, saveNum = 1, countNum = 0, lastNum = 0;\nint copyNum = num;\nwhile (num != 0) {\n lastNum = num % 10;\n num /= 10;\n if (lastNum == 0) {\n // 如果是0那么正好是少了一次所以num不加1了\n countNum += num * saveNum;\n } else if (lastNum == 1) {\n // 如果是1说明当前数内少了一次所以num不加1,而且当前1所在位置\n // 有1的个数,就是去除当前1最高位,剩下位数,的个数。\n countNum += num * saveNum + copyNum % saveNum + 1;\n } else {\n // 如果非1非0.直接用公式计算\n // abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...\n countNum += (num + 1) * saveNum;\n }\n saveNum *= 10;\n}\nSystem.out.println(\"1的个数:\" + countNum);\nSystem.out.println(\"计算耗时:\" + (System.currentTimeMillis() - startTime) + \"毫秒\");\n```\n\n在《编程之美》一书中还不只这一种算法,感兴趣的小伙伴可以查阅。*但自己折腾实现后的兴奋感更强哦!*\n\n### 3. 耗时曲线对比\n\n按照两种不同方式的实现逻辑,我们来计算1000、10000、10000到一个亿,求1出现的次数,看看两种方式的耗时曲线。\n\n\n\n- for循环随着数量的不断增大后,已经趋近于无法使用了。\n- 算法逻辑依靠的计算公式,所以无论增加多少基本都会在1~2毫秒内计算完成。\n\n**那么**,你的代码中是否也有类似的地方。如果使用算法逻辑配合适合的数据结构,是否可以替代一些for循环的计算方式,来使整个实现过程的时间复杂度降低。\n\n## 四、Java中的算法运用\n\n在 Java 的 JDK 实现中有很多数学知识的运用,包括数组、链表、红黑树的数据结构以及相应的实现类ArrayList、Linkedlist、HashMap等。当你深入的了解这些类的实现后,会发现它们其实就是使用代码来实现数学逻辑而已。*就像你使用数学公式来计算数学题一样*\n\n接下来小傅哥就给你介绍几个隐藏在我们代码中的数学知识。\n\n### 1. HashMap的扰动函数\n\n**未使用扰动函数**\n\n\n\n**已使用扰动函数**\n\n\n\n**扰动函数公式**\n\n```java\nstatic final int hash(Object key) {\n int h;\n return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n}\n```\n\n- **描述**:以上这段代码是HashMap中用于获取hash值的扰动函数实现代码。*HashMap通过哈希值与桶定位坐标* 那么直接获取哈希值就好了,这里为什么要做一次扰动呢?\n- **作用**:为了证明扰动函数的作用,这里选取了10万单词计算哈希值分布在128个格子里。之后把这128个格子中的数据做图表展示。从实现数据可以看到,在使用扰动函数后,曲线更加平稳了。那么,也就是扰动后哈希碰撞会更小。\n- **用途**:当你有需要把数据散列分散到不同格子或者空间时,又不希望有太严重的碰撞,那么使用扰动函数就非常有必要了。比如你做的一个数据库路由,在分库分表时也是尽可能的要做到散列的。\n\n### 2. 斐波那契(Fibonacci)散列法\n\n\n\n- **描述**:在 ThreadLocal 类中的数据存放,使用的是斐波那契(Fibonacci)散列法 + 开放寻址。之所以使用斐波那契数列,是为了让数据更加散列,减少哈希碰撞。具体来自数学公式的计算求值,**公式**:`f(k) = ((k * 2654435769) >> X) << Y对于常见的32位整数而言,也就是 f(k) = (k * 2654435769) >> 28`\n- **作用**:与 HashMap 相比,ThreadLocal的数据结构只有数组,并没有链表和红黑树部分。而且经过我们测试验证,斐波那契散列的效果更好,也更适合 ThreadLocal。\n- **用途**:如果你的代码逻辑中需要存储类似 ThreadLocal 的数据结构,又不想有严重哈希碰撞,那么就可以使用 斐波那契(Fibonacci)散列法。其实除此之外还有,`除法散列法`、`平方散列法`、`随机数法`等。\n\n### 3. 梅森旋转算法(Mersenne twister)\n\n\n\n```java\n// Initializes mt[N] with a simple integer seed. This method is\n// required as part of the Mersenne Twister algorithm but need\n// not be made public.\nprivate final void setSeed(int seed) {\n // Annoying runtime check for initialisation of internal data\n // caused by java.util.Random invoking setSeed() during init.\n // This is unavoidable because no fields in our instance will\n // have been initialised at this point, not even if the code\n // were placed at the declaration of the member variable.\n if (mt == null) mt = new int[N];\n // ---- Begin Mersenne Twister Algorithm ----\n mt[0] = seed;\n for (mti = 1; mti < N; mti++) {\n mt[mti] = (MAGIC_FACTOR1 * (mt[mti-1] 6 (mt[mti-1] >>> 30)) + mti);\n }\n // ---- End Mersenne Twister Algorithm ----\n}\n```\n\n>梅森旋转算法(Mersenne twister)是一个伪随机数发生算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性递归。可以快速产生高质量的伪随机数,修正了古典随机数发生算法的很多缺陷。 最为广泛使用Mersenne Twister的一种变体是MT19937,可以产生32位整数序列。\n\n- **描述**:梅森旋转算法分为三个阶段,获得基础的梅森旋转链、对于旋转链进行旋转算法、对于旋转算法所得的结果进行处理。\n- **用途**:梅森旋转算法是R、Python、Ruby、IDL、Free Pascal、PHP、Maple、Matlab、GNU多重精度运算库和GSL的默认伪随机数产生器。从C++11开始,C++也可以使用这种算法。在Boost C++,Glib和NAG数值库中,作为插件提供。\n\n## 五、程序员数学入门\n\n与接触到一个有难度的知识点学起来辛苦相比,是自己不知道自己不会什么!*就像上学时候老师说,你不会的就问我。我不会啥?我从哪问?一样一样的!*\n\n代码是对数学逻辑的实现,简单的逻辑调用关系是很容易看明白的。但还有那部分你可能不知道的数学逻辑时,就很难看懂了。比如:扰动函数、负载因子、斐波那契(Fibonacci)等,这些知识点的学习都需要对数学知识进行验证,否则也就学个概念,背个理论。\n\n书到用时方恨少,在下还是个宝宝!\n\n那如果你想深入的学习下程序员应该会的数学,推荐给你一位科技博主 Jeremy Kun 花了4年时间,写成一本书 **《程序员数学入门》**。\n\n\n\n这本书为程序员提供了大量精简后数学知识,包括:多项式、集合、图论、群论、微积分和线性代数等。同时在wiki部分还包括了抽象代数、离散数学、傅里叶分析和拓扑学等。\n\n\n\n作者表示,如果你本科学过一些数学知识,那么本书还是挺适合你的,不会有什么难度。书中的前三章是基础数学内容,往后的难度依次递增。\n\n- 书籍获取:关注公众号:bugstack虫洞栈,回复:`程序员数学`,下载这本书\n- 在线Wiki:[https://jeremykun.com/primers/](https://jeremykun.com/primers/)\n\n## 六、总结\n\n- Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. [https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html)\n- 单纯的只会数学写不了代码,能写代码的不懂数学只能是CRUD码农。数学知识帮助你设计数据结构和实现算法逻辑,代码能力帮你驾驭设计模式和架构模型。多方面的知识结合和使用才是码农和工程师的主要区别,也是是否拥有核心竞争力的关键点。\n- 学习知识有时候看不到前面的路有多远,但哪怕是个泥坑,只要你不停的蠕动、折腾、翻滚,也能抓出一条泥鳅。`知识的路上是发现知识的快乐,还学会知识的成就感,不断的促使你前行`。\n\n## 七、系列推荐\n\n- [互联网大厂,线上研发事故总结!](https://bugstack.cn/itstack-code-life/2021/01/10/%E6%8F%A1%E8%8D%89-%E8%BF%99%E4%BA%9B%E7%A0%94%E5%8F%91%E4%BA%8B%E6%95%8530-%E6%88%91%E9%83%BD%E5%B9%B2%E8%BF%87.html)\n- [码德,这不就是产品给我留的数学作业!](https://bugstack.cn/itstack-code-life/2020/12/13/%E7%A0%81%E5%BE%B7%E9%9C%80%E6%B1%82-%E8%BF%99%E4%B8%8D%E5%B0%B1%E6%98%AF%E4%BA%A7%E5%93%81%E7%BB%99%E6%88%91%E7%95%99%E7%9A%84%E6%95%B0%E5%AD%A6%E4%BD%9C%E4%B8%9A.html)\n- [HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习](https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html)\n- [Netty实战,1比1仿桌面版微信聊天](https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html)\n- [重学 Java 设计模式,全网最火的设计模式 17万+下载](https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html)\n\n","source":"_posts/数学,离一个程序员有多近?.md","raw":"---\nlayout: post\ntitle: \"数学,离一个程序员有多近?\"\ndate: 2021-01-17 21:20\ncomments: true\ntags: \n\t- java \n\t- 数据结构 \n\t- 算法逻辑 \n---\n\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`数学离程序员有多近?`\n\nifelse也好、for循环也罢,代码可以说就是对**数学逻辑的具体实现**。所以敲代码的程序员几乎就离不开数学,难易不同而已。\n\n那数学不好就写不了代码吗😳?不,一样可以写代码,可以写出更多的`CRUD`出来。那你不要总觉得是产品需求简单所以你的实现过程才变成了增删改查,往往也是因为你还不具备可扩展、易维护、高性能的代码实现方案落地能力,才使得你小小年纪写出了更多的`CRUD`!\n\n<!-- more -->\n\n与一锥子买卖的小作坊相比,大厂和超级大厂更会注重数学能力。\n\n\n\n**2004年**,在硅谷的交通动脉 101 公路上突然出现一块巨大的广告牌,上面是一道数学题:` {e 的连续数字中最先出现的 10 位质数}`.com。\n\n广告:这里的 e 是数学常数,自然对数的底数,无限不循环小数。这道题的意思就是,找出 e 中最先出现的 10 位质数,然后可以得出一个网址。进入这个网址会看到 Google 为你出的第二道数学题,成功解锁这步 Google 会告诉你,`我们或许是”志同道合“的人`,你可以将简历发到这个邮箱,我们一起做点改变世界的事情。\n\n*计算 e 值可以通过泰勒公式推导出来:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推导计算过程还包括`埃拉托色尼筛选法(the Sieve of Eratosthenes)`、`线性筛选法`的使用。感兴趣的小伙伴可以用代码实现下。*\n\n## 二、把代码写好的四步\n\n`业务提需求、产品定方案、研发做实现。`最终这个系统开发的怎么样是由三方共同决定的!\n\n- 地基挖的不好,楼就盖不高\n- 砖头摆放不巧,楼就容易倒\n- 水电走线不妙,楼就危险了\n- 格局设计不行,楼就卖不掉\n\n这里的地基、砖头、水电、格局,对应的就是,数据结构、算法逻辑、设计模式、系统架构。从下到上相互依赖、相互配合,只有这一层做好,下一层才好做!\n\n\n\n- **数据结构**:高矮胖瘦、长宽扁细,数据的存放方式,是一套程序开发的核心基础。不合理的设计往往是从数据结构开始,哪怕你仅仅是使用数据库存放业务信息,也一样会影响到将来各类数据的查询、汇总等实现逻辑的难易。\n- **算法逻辑**:是对数据结构的使用,合适的数据结构会让算法实现过程降低时间复杂度。可能你现在的多层for循环在合适的算法过程下,能被优化为更简单的方式获取数据。*注意:算法逻辑实现,并不一定就是排序、归并,还有你实际业务的处理流程。*\n- **设计模式**:可以这么说,不使用设计模式你一样能写代码。但你愿意看到满屏幕的ifelse判断调用,还是喜欢像膏药一样的代码,粘贴来复制去?那么设计模式这套通用场景的解决方案,就是为你剔除掉代码实现过程中的恶心部分,让整套程序更加易维护、易扩展。*就是开发完一个月,你看它你还认识!*\n- **系统架构**:描述的是三层MVC,还是四层DDD。我对这个的理解就是家里的三居还是四局格局,MVC是我们经常用的大家都熟悉,DDD无非就是家里多了个书房,把各自属于哪一个屋子的摆件规整到各自屋子里。*那么乱放是什么效果呢,就是自动洗屁屁马桶🚽给按到厨房了,再贵也格楞子!* 好,那么我们在延展下,如果你的卫生间没有流出下水道咋办?是不这个地方的数据结构就是设计缺失的,而到后面再想扩展就难了吧!\n\n所以,研发在承接业务需求、实现产品方案的时候。压根就不只是在一个房子的三居或者四居格局里,开始随意码砖。\n\n没有合理的数据结构、没有优化的算法逻辑、没有运用的设计模式,最终都会影响到整个系统架构变得臃肿不堪,调用混乱。在以后附加、迭代、新增的需求下,会让整个系统问题不断的放大,当你想用重构时,就有着千丝万缕般调用关系。 *重构就不如重写了!*\n\n## 三、for循环没算法快\n\n在《编程之美》一书中,有这样一道题。求:1~n中,1出现的次数。比如:1~10,1出现了两次。\n\n### 1. for 循环实现\n\n```java\nlong startTime = System.currentTimeMillis();\nint count = 0;\nfor (int i = 1; i <= 10000000; i++) {\n String str = String.valueOf(i);\n for (int j = 0; j < str.length(); j++) {\n if (str.charAt(j) == 49) {\n count++;\n }\n }\n}\nSystem.out.println(\"1的个数:\" + count);\nSystem.out.println(\"计算耗时:\" + (System.currentTimeMillis() - startTime) + \"毫秒\");\n```\n\n使用 for 循环的实现过程很好理解,就是往死了循环。之后把循环到的数字按照字符串拆解,判断每一位是不是数字,是就+1。这个过程很简单,但是时间复杂很高。\n\n### 2. 算法逻辑实现\n\n\n\n如图 20-3 所示,其实我们能发现这个1的个数在100、1000、10000中是有规则的循环出现的。11、12、13、14或者21、31、41、51,以及单个的1出现。最终可以得出通用公式:`abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...`,abcd代表位数。另外在实现的过程还需要考虑比如不足100等情况,例如98、1232等。\n\n**实现过程**\n\n```java\nlong startTime = System.currentTimeMillis();\nint num = 10000000, saveNum = 1, countNum = 0, lastNum = 0;\nint copyNum = num;\nwhile (num != 0) {\n lastNum = num % 10;\n num /= 10;\n if (lastNum == 0) {\n // 如果是0那么正好是少了一次所以num不加1了\n countNum += num * saveNum;\n } else if (lastNum == 1) {\n // 如果是1说明当前数内少了一次所以num不加1,而且当前1所在位置\n // 有1的个数,就是去除当前1最高位,剩下位数,的个数。\n countNum += num * saveNum + copyNum % saveNum + 1;\n } else {\n // 如果非1非0.直接用公式计算\n // abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...\n countNum += (num + 1) * saveNum;\n }\n saveNum *= 10;\n}\nSystem.out.println(\"1的个数:\" + countNum);\nSystem.out.println(\"计算耗时:\" + (System.currentTimeMillis() - startTime) + \"毫秒\");\n```\n\n在《编程之美》一书中还不只这一种算法,感兴趣的小伙伴可以查阅。*但自己折腾实现后的兴奋感更强哦!*\n\n### 3. 耗时曲线对比\n\n按照两种不同方式的实现逻辑,我们来计算1000、10000、10000到一个亿,求1出现的次数,看看两种方式的耗时曲线。\n\n\n\n- for循环随着数量的不断增大后,已经趋近于无法使用了。\n- 算法逻辑依靠的计算公式,所以无论增加多少基本都会在1~2毫秒内计算完成。\n\n**那么**,你的代码中是否也有类似的地方。如果使用算法逻辑配合适合的数据结构,是否可以替代一些for循环的计算方式,来使整个实现过程的时间复杂度降低。\n\n## 四、Java中的算法运用\n\n在 Java 的 JDK 实现中有很多数学知识的运用,包括数组、链表、红黑树的数据结构以及相应的实现类ArrayList、Linkedlist、HashMap等。当你深入的了解这些类的实现后,会发现它们其实就是使用代码来实现数学逻辑而已。*就像你使用数学公式来计算数学题一样*\n\n接下来小傅哥就给你介绍几个隐藏在我们代码中的数学知识。\n\n### 1. HashMap的扰动函数\n\n**未使用扰动函数**\n\n\n\n**已使用扰动函数**\n\n\n\n**扰动函数公式**\n\n```java\nstatic final int hash(Object key) {\n int h;\n return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n}\n```\n\n- **描述**:以上这段代码是HashMap中用于获取hash值的扰动函数实现代码。*HashMap通过哈希值与桶定位坐标* 那么直接获取哈希值就好了,这里为什么要做一次扰动呢?\n- **作用**:为了证明扰动函数的作用,这里选取了10万单词计算哈希值分布在128个格子里。之后把这128个格子中的数据做图表展示。从实现数据可以看到,在使用扰动函数后,曲线更加平稳了。那么,也就是扰动后哈希碰撞会更小。\n- **用途**:当你有需要把数据散列分散到不同格子或者空间时,又不希望有太严重的碰撞,那么使用扰动函数就非常有必要了。比如你做的一个数据库路由,在分库分表时也是尽可能的要做到散列的。\n\n### 2. 斐波那契(Fibonacci)散列法\n\n\n\n- **描述**:在 ThreadLocal 类中的数据存放,使用的是斐波那契(Fibonacci)散列法 + 开放寻址。之所以使用斐波那契数列,是为了让数据更加散列,减少哈希碰撞。具体来自数学公式的计算求值,**公式**:`f(k) = ((k * 2654435769) >> X) << Y对于常见的32位整数而言,也就是 f(k) = (k * 2654435769) >> 28`\n- **作用**:与 HashMap 相比,ThreadLocal的数据结构只有数组,并没有链表和红黑树部分。而且经过我们测试验证,斐波那契散列的效果更好,也更适合 ThreadLocal。\n- **用途**:如果你的代码逻辑中需要存储类似 ThreadLocal 的数据结构,又不想有严重哈希碰撞,那么就可以使用 斐波那契(Fibonacci)散列法。其实除此之外还有,`除法散列法`、`平方散列法`、`随机数法`等。\n\n### 3. 梅森旋转算法(Mersenne twister)\n\n\n\n```java\n// Initializes mt[N] with a simple integer seed. This method is\n// required as part of the Mersenne Twister algorithm but need\n// not be made public.\nprivate final void setSeed(int seed) {\n // Annoying runtime check for initialisation of internal data\n // caused by java.util.Random invoking setSeed() during init.\n // This is unavoidable because no fields in our instance will\n // have been initialised at this point, not even if the code\n // were placed at the declaration of the member variable.\n if (mt == null) mt = new int[N];\n // ---- Begin Mersenne Twister Algorithm ----\n mt[0] = seed;\n for (mti = 1; mti < N; mti++) {\n mt[mti] = (MAGIC_FACTOR1 * (mt[mti-1] 6 (mt[mti-1] >>> 30)) + mti);\n }\n // ---- End Mersenne Twister Algorithm ----\n}\n```\n\n>梅森旋转算法(Mersenne twister)是一个伪随机数发生算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性递归。可以快速产生高质量的伪随机数,修正了古典随机数发生算法的很多缺陷。 最为广泛使用Mersenne Twister的一种变体是MT19937,可以产生32位整数序列。\n\n- **描述**:梅森旋转算法分为三个阶段,获得基础的梅森旋转链、对于旋转链进行旋转算法、对于旋转算法所得的结果进行处理。\n- **用途**:梅森旋转算法是R、Python、Ruby、IDL、Free Pascal、PHP、Maple、Matlab、GNU多重精度运算库和GSL的默认伪随机数产生器。从C++11开始,C++也可以使用这种算法。在Boost C++,Glib和NAG数值库中,作为插件提供。\n\n## 五、程序员数学入门\n\n与接触到一个有难度的知识点学起来辛苦相比,是自己不知道自己不会什么!*就像上学时候老师说,你不会的就问我。我不会啥?我从哪问?一样一样的!*\n\n代码是对数学逻辑的实现,简单的逻辑调用关系是很容易看明白的。但还有那部分你可能不知道的数学逻辑时,就很难看懂了。比如:扰动函数、负载因子、斐波那契(Fibonacci)等,这些知识点的学习都需要对数学知识进行验证,否则也就学个概念,背个理论。\n\n书到用时方恨少,在下还是个宝宝!\n\n那如果你想深入的学习下程序员应该会的数学,推荐给你一位科技博主 Jeremy Kun 花了4年时间,写成一本书 **《程序员数学入门》**。\n\n\n\n这本书为程序员提供了大量精简后数学知识,包括:多项式、集合、图论、群论、微积分和线性代数等。同时在wiki部分还包括了抽象代数、离散数学、傅里叶分析和拓扑学等。\n\n\n\n作者表示,如果你本科学过一些数学知识,那么本书还是挺适合你的,不会有什么难度。书中的前三章是基础数学内容,往后的难度依次递增。\n\n- 书籍获取:关注公众号:bugstack虫洞栈,回复:`程序员数学`,下载这本书\n- 在线Wiki:[https://jeremykun.com/primers/](https://jeremykun.com/primers/)\n\n## 六、总结\n\n- Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. [https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html)\n- 单纯的只会数学写不了代码,能写代码的不懂数学只能是CRUD码农。数学知识帮助你设计数据结构和实现算法逻辑,代码能力帮你驾驭设计模式和架构模型。多方面的知识结合和使用才是码农和工程师的主要区别,也是是否拥有核心竞争力的关键点。\n- 学习知识有时候看不到前面的路有多远,但哪怕是个泥坑,只要你不停的蠕动、折腾、翻滚,也能抓出一条泥鳅。`知识的路上是发现知识的快乐,还学会知识的成就感,不断的促使你前行`。\n\n## 七、系列推荐\n\n- [互联网大厂,线上研发事故总结!](https://bugstack.cn/itstack-code-life/2021/01/10/%E6%8F%A1%E8%8D%89-%E8%BF%99%E4%BA%9B%E7%A0%94%E5%8F%91%E4%BA%8B%E6%95%8530-%E6%88%91%E9%83%BD%E5%B9%B2%E8%BF%87.html)\n- [码德,这不就是产品给我留的数学作业!](https://bugstack.cn/itstack-code-life/2020/12/13/%E7%A0%81%E5%BE%B7%E9%9C%80%E6%B1%82-%E8%BF%99%E4%B8%8D%E5%B0%B1%E6%98%AF%E4%BA%A7%E5%93%81%E7%BB%99%E6%88%91%E7%95%99%E7%9A%84%E6%95%B0%E5%AD%A6%E4%BD%9C%E4%B8%9A.html)\n- [HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习](https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html)\n- [Netty实战,1比1仿桌面版微信聊天](https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html)\n- [重学 Java 设计模式,全网最火的设计模式 17万+下载](https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html)\n\n","slug":"数学,离一个程序员有多近?","published":1,"updated":"2021-01-23T08:35:14.307Z","_id":"ckk9foalu0000c69l1eq87pjp","photos":[],"link":"","content":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>数学离程序员有多近?</code></p>\n<p>ifelse也好、for循环也罢,代码可以说就是对<strong>数学逻辑的具体实现</strong>。所以敲代码的程序员几乎就离不开数学,难易不同而已。</p>\n<p>那数学不好就写不了代码吗😳?不,一样可以写代码,可以写出更多的<code>CRUD</code>出来。那你不要总觉得是产品需求简单所以你的实现过程才变成了增删改查,往往也是因为你还不具备可扩展、易维护、高性能的代码实现方案落地能力,才使得你小小年纪写出了更多的<code>CRUD</code>!</p>\n<a id=\"more\"></a>\n\n<p>与一锥子买卖的小作坊相比,大厂和超级大厂更会注重数学能力。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-1.png\" alt=\"first 10-digit prime found in consecutive digits of e\"></p>\n<p><strong>2004年</strong>,在硅谷的交通动脉 101 公路上突然出现一块巨大的广告牌,上面是一道数学题:<code> {e 的连续数字中最先出现的 10 位质数}</code>.com。</p>\n<p>广告:这里的 e 是数学常数,自然对数的底数,无限不循环小数。这道题的意思就是,找出 e 中最先出现的 10 位质数,然后可以得出一个网址。进入这个网址会看到 Google 为你出的第二道数学题,成功解锁这步 Google 会告诉你,<code>我们或许是”志同道合“的人</code>,你可以将简历发到这个邮箱,我们一起做点改变世界的事情。</p>\n<p><em>计算 e 值可以通过泰勒公式推导出来:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推导计算过程还包括<code>埃拉托色尼筛选法(the Sieve of Eratosthenes)</code>、<code>线性筛选法</code>的使用。感兴趣的小伙伴可以用代码实现下。</em></p>\n<h2 id=\"二、把代码写好的四步\"><a href=\"#二、把代码写好的四步\" class=\"headerlink\" title=\"二、把代码写好的四步\"></a>二、把代码写好的四步</h2><p><code>业务提需求、产品定方案、研发做实现。</code>最终这个系统开发的怎么样是由三方共同决定的!</p>\n<ul>\n<li>地基挖的不好,楼就盖不高</li>\n<li>砖头摆放不巧,楼就容易倒</li>\n<li>水电走线不妙,楼就危险了</li>\n<li>格局设计不行,楼就卖不掉</li>\n</ul>\n<p>这里的地基、砖头、水电、格局,对应的就是,数据结构、算法逻辑、设计模式、系统架构。从下到上相互依赖、相互配合,只有这一层做好,下一层才好做!</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-2.png\" alt=\"图 20-2 代码实现过程分层\"></p>\n<ul>\n<li><strong>数据结构</strong>:高矮胖瘦、长宽扁细,数据的存放方式,是一套程序开发的核心基础。不合理的设计往往是从数据结构开始,哪怕你仅仅是使用数据库存放业务信息,也一样会影响到将来各类数据的查询、汇总等实现逻辑的难易。</li>\n<li><strong>算法逻辑</strong>:是对数据结构的使用,合适的数据结构会让算法实现过程降低时间复杂度。可能你现在的多层for循环在合适的算法过程下,能被优化为更简单的方式获取数据。<em>注意:算法逻辑实现,并不一定就是排序、归并,还有你实际业务的处理流程。</em></li>\n<li><strong>设计模式</strong>:可以这么说,不使用设计模式你一样能写代码。但你愿意看到满屏幕的ifelse判断调用,还是喜欢像膏药一样的代码,粘贴来复制去?那么设计模式这套通用场景的解决方案,就是为你剔除掉代码实现过程中的恶心部分,让整套程序更加易维护、易扩展。<em>就是开发完一个月,你看它你还认识!</em></li>\n<li><strong>系统架构</strong>:描述的是三层MVC,还是四层DDD。我对这个的理解就是家里的三居还是四局格局,MVC是我们经常用的大家都熟悉,DDD无非就是家里多了个书房,把各自属于哪一个屋子的摆件规整到各自屋子里。<em>那么乱放是什么效果呢,就是自动洗屁屁马桶🚽给按到厨房了,再贵也格楞子!</em> 好,那么我们在延展下,如果你的卫生间没有流出下水道咋办?是不这个地方的数据结构就是设计缺失的,而到后面再想扩展就难了吧!</li>\n</ul>\n<p>所以,研发在承接业务需求、实现产品方案的时候。压根就不只是在一个房子的三居或者四居格局里,开始随意码砖。</p>\n<p>没有合理的数据结构、没有优化的算法逻辑、没有运用的设计模式,最终都会影响到整个系统架构变得臃肿不堪,调用混乱。在以后附加、迭代、新增的需求下,会让整个系统问题不断的放大,当你想用重构时,就有着千丝万缕般调用关系。 <em>重构就不如重写了!</em></p>\n<h2 id=\"三、for循环没算法快\"><a href=\"#三、for循环没算法快\" class=\"headerlink\" title=\"三、for循环没算法快\"></a>三、for循环没算法快</h2><p>在《编程之美》一书中,有这样一道题。求:1<del>n中,1出现的次数。比如:1</del>10,1出现了两次。</p>\n<h3 id=\"1-for-循环实现\"><a href=\"#1-for-循环实现\" class=\"headerlink\" title=\"1. for 循环实现\"></a>1. for 循环实现</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"><span class=\"keyword\">int</span> count = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">1</span>; i <= <span class=\"number\">10000000</span>; i++) {</span><br><span class=\"line\"> String str = String.valueOf(i);</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = <span class=\"number\">0</span>; j < str.length(); j++) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (str.charAt(j) == <span class=\"number\">49</span>) {</span><br><span class=\"line\"> count++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\">System.out.println(<span class=\"string\">"1的个数:"</span> + count);</span><br><span class=\"line\">System.out.println(<span class=\"string\">"计算耗时:"</span> + (System.currentTimeMillis() - startTime) + <span class=\"string\">"毫秒"</span>);</span><br></pre></td></tr></table></figure>\n<p>使用 for 循环的实现过程很好理解,就是往死了循环。之后把循环到的数字按照字符串拆解,判断每一位是不是数字,是就+1。这个过程很简单,但是时间复杂很高。</p>\n<h3 id=\"2-算法逻辑实现\"><a href=\"#2-算法逻辑实现\" class=\"headerlink\" title=\"2. 算法逻辑实现\"></a>2. 算法逻辑实现</h3><p><img src=\"https://bugstack.cn/assets/images/2020/all-20-3.png\" alt=\"图 20-3 1的个数循环规则\"></p>\n<p>如图 20-3 所示,其实我们能发现这个1的个数在100、1000、10000中是有规则的循环出现的。11、12、13、14或者21、31、41、51,以及单个的1出现。最终可以得出通用公式:<code>abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...</code>,abcd代表位数。另外在实现的过程还需要考虑比如不足100等情况,例如98、1232等。</p>\n<p><strong>实现过程</strong></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"><span class=\"keyword\">int</span> num = <span class=\"number\">10000000</span>, saveNum = <span class=\"number\">1</span>, countNum = <span class=\"number\">0</span>, lastNum = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"keyword\">int</span> copyNum = num;</span><br><span class=\"line\"><span class=\"keyword\">while</span> (num != <span class=\"number\">0</span>) {</span><br><span class=\"line\"> lastNum = num % <span class=\"number\">10</span>;</span><br><span class=\"line\"> num /= <span class=\"number\">10</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (lastNum == <span class=\"number\">0</span>) {</span><br><span class=\"line\"> <span class=\"comment\">// 如果是0那么正好是少了一次所以num不加1了</span></span><br><span class=\"line\"> countNum += num * saveNum;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> (lastNum == <span class=\"number\">1</span>) {</span><br><span class=\"line\"> <span class=\"comment\">// 如果是1说明当前数内少了一次所以num不加1,而且当前1所在位置</span></span><br><span class=\"line\"> <span class=\"comment\">// 有1的个数,就是去除当前1最高位,剩下位数,的个数。</span></span><br><span class=\"line\"> countNum += num * saveNum + copyNum % saveNum + <span class=\"number\">1</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> <span class=\"comment\">// 如果非1非0.直接用公式计算</span></span><br><span class=\"line\"> <span class=\"comment\">// abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...</span></span><br><span class=\"line\"> countNum += (num + <span class=\"number\">1</span>) * saveNum;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> saveNum *= <span class=\"number\">10</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\">System.out.println(<span class=\"string\">"1的个数:"</span> + countNum);</span><br><span class=\"line\">System.out.println(<span class=\"string\">"计算耗时:"</span> + (System.currentTimeMillis() - startTime) + <span class=\"string\">"毫秒"</span>);</span><br></pre></td></tr></table></figure>\n<p>在《编程之美》一书中还不只这一种算法,感兴趣的小伙伴可以查阅。<em>但自己折腾实现后的兴奋感更强哦!</em></p>\n<h3 id=\"3-耗时曲线对比\"><a href=\"#3-耗时曲线对比\" class=\"headerlink\" title=\"3. 耗时曲线对比\"></a>3. 耗时曲线对比</h3><p>按照两种不同方式的实现逻辑,我们来计算1000、10000、10000到一个亿,求1出现的次数,看看两种方式的耗时曲线。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-4.png\" alt=\"图 20-4 耗时曲线对比\"></p>\n<ul>\n<li>for循环随着数量的不断增大后,已经趋近于无法使用了。</li>\n<li>算法逻辑依靠的计算公式,所以无论增加多少基本都会在1~2毫秒内计算完成。</li>\n</ul>\n<p><strong>那么</strong>,你的代码中是否也有类似的地方。如果使用算法逻辑配合适合的数据结构,是否可以替代一些for循环的计算方式,来使整个实现过程的时间复杂度降低。</p>\n<h2 id=\"四、Java中的算法运用\"><a href=\"#四、Java中的算法运用\" class=\"headerlink\" title=\"四、Java中的算法运用\"></a>四、Java中的算法运用</h2><p>在 Java 的 JDK 实现中有很多数学知识的运用,包括数组、链表、红黑树的数据结构以及相应的实现类ArrayList、Linkedlist、HashMap等。当你深入的了解这些类的实现后,会发现它们其实就是使用代码来实现数学逻辑而已。<em>就像你使用数学公式来计算数学题一样</em></p>\n<p>接下来小傅哥就给你介绍几个隐藏在我们代码中的数学知识。</p>\n<h3 id=\"1-HashMap的扰动函数\"><a href=\"#1-HashMap的扰动函数\" class=\"headerlink\" title=\"1. HashMap的扰动函数\"></a>1. HashMap的扰动函数</h3><p><strong>未使用扰动函数</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-4-03.png\" alt=\"未使用扰动函数,数据分布\"></p>\n<p><strong>已使用扰动函数</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-4-04.png\" alt=\"未使用扰动函数,数据分布\"></p>\n<p><strong>扰动函数公式</strong></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">static</span> <span class=\"keyword\">final</span> <span class=\"keyword\">int</span> <span class=\"title\">hash</span><span class=\"params\">(Object key)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span> h;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (key == <span class=\"keyword\">null</span>) ? <span class=\"number\">0</span> : (h = key.hashCode()) ^ (h >>> <span class=\"number\">16</span>);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li><strong>描述</strong>:以上这段代码是HashMap中用于获取hash值的扰动函数实现代码。<em>HashMap通过哈希值与桶定位坐标</em> 那么直接获取哈希值就好了,这里为什么要做一次扰动呢?</li>\n<li><strong>作用</strong>:为了证明扰动函数的作用,这里选取了10万单词计算哈希值分布在128个格子里。之后把这128个格子中的数据做图表展示。从实现数据可以看到,在使用扰动函数后,曲线更加平稳了。那么,也就是扰动后哈希碰撞会更小。</li>\n<li><strong>用途</strong>:当你有需要把数据散列分散到不同格子或者空间时,又不希望有太严重的碰撞,那么使用扰动函数就非常有必要了。比如你做的一个数据库路由,在分库分表时也是尽可能的要做到散列的。</li>\n</ul>\n<h3 id=\"2-斐波那契(Fibonacci)散列法\"><a href=\"#2-斐波那契(Fibonacci)散列法\" class=\"headerlink\" title=\"2. 斐波那契(Fibonacci)散列法\"></a>2. 斐波那契(Fibonacci)散列法</h3><p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-13-02.png\" alt=\"ThreadLocal 中 斐波那契(Fibonacci)散列法\"></p>\n<ul>\n<li><strong>描述</strong>:在 ThreadLocal 类中的数据存放,使用的是斐波那契(Fibonacci)散列法 + 开放寻址。之所以使用斐波那契数列,是为了让数据更加散列,减少哈希碰撞。具体来自数学公式的计算求值,<strong>公式</strong>:<code>f(k) = ((k * 2654435769) >> X) << Y对于常见的32位整数而言,也就是 f(k) = (k * 2654435769) >> 28</code></li>\n<li><strong>作用</strong>:与 HashMap 相比,ThreadLocal的数据结构只有数组,并没有链表和红黑树部分。而且经过我们测试验证,斐波那契散列的效果更好,也更适合 ThreadLocal。</li>\n<li><strong>用途</strong>:如果你的代码逻辑中需要存储类似 ThreadLocal 的数据结构,又不想有严重哈希碰撞,那么就可以使用 斐波那契(Fibonacci)散列法。其实除此之外还有,<code>除法散列法</code>、<code>平方散列法</code>、<code>随机数法</code>等。</li>\n</ul>\n<h3 id=\"3-梅森旋转算法(Mersenne-twister)\"><a href=\"#3-梅森旋转算法(Mersenne-twister)\" class=\"headerlink\" title=\"3. 梅森旋转算法(Mersenne twister)\"></a>3. 梅森旋转算法(Mersenne twister)</h3><p><img src=\"https://bugstack.cn/assets/images/2020/all-20-5.png\" alt=\"梅森旋转算法的三个阶段,来自CSDN博客网图\"></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// Initializes mt[N] with a simple integer seed. This method is</span></span><br><span class=\"line\"><span class=\"comment\">// required as part of the Mersenne Twister algorithm but need</span></span><br><span class=\"line\"><span class=\"comment\">// not be made public.</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">private</span> <span class=\"keyword\">final</span> <span class=\"keyword\">void</span> <span class=\"title\">setSeed</span><span class=\"params\">(<span class=\"keyword\">int</span> seed)</span> </span>{</span><br><span class=\"line\"> <span class=\"comment\">// Annoying runtime check for initialisation of internal data</span></span><br><span class=\"line\"> <span class=\"comment\">// caused by java.util.Random invoking setSeed() during init.</span></span><br><span class=\"line\"> <span class=\"comment\">// This is unavoidable because no fields in our instance will</span></span><br><span class=\"line\"> <span class=\"comment\">// have been initialised at this point, not even if the code</span></span><br><span class=\"line\"> <span class=\"comment\">// were placed at the declaration of the member variable.</span></span><br><span class=\"line\"> <span class=\"keyword\">if</span> (mt == <span class=\"keyword\">null</span>) mt = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[N];</span><br><span class=\"line\"> <span class=\"comment\">// ---- Begin Mersenne Twister Algorithm ----</span></span><br><span class=\"line\"> mt[<span class=\"number\">0</span>] = seed;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (mti = <span class=\"number\">1</span>; mti < N; mti++) {</span><br><span class=\"line\"> mt[mti] = (MAGIC_FACTOR1 * (mt[mti-<span class=\"number\">1</span>] <span class=\"number\">6</span> (mt[mti-<span class=\"number\">1</span>] >>> <span class=\"number\">30</span>)) + mti);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">// ---- End Mersenne Twister Algorithm ----</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>梅森旋转算法(Mersenne twister)是一个伪随机数发生算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性递归。可以快速产生高质量的伪随机数,修正了古典随机数发生算法的很多缺陷。 最为广泛使用Mersenne Twister的一种变体是MT19937,可以产生32位整数序列。</p>\n</blockquote>\n<ul>\n<li><strong>描述</strong>:梅森旋转算法分为三个阶段,获得基础的梅森旋转链、对于旋转链进行旋转算法、对于旋转算法所得的结果进行处理。</li>\n<li><strong>用途</strong>:梅森旋转算法是R、Python、Ruby、IDL、Free Pascal、PHP、Maple、Matlab、GNU多重精度运算库和GSL的默认伪随机数产生器。从C++11开始,C++也可以使用这种算法。在Boost C++,Glib和NAG数值库中,作为插件提供。</li>\n</ul>\n<h2 id=\"五、程序员数学入门\"><a href=\"#五、程序员数学入门\" class=\"headerlink\" title=\"五、程序员数学入门\"></a>五、程序员数学入门</h2><p>与接触到一个有难度的知识点学起来辛苦相比,是自己不知道自己不会什么!<em>就像上学时候老师说,你不会的就问我。我不会啥?我从哪问?一样一样的!</em></p>\n<p>代码是对数学逻辑的实现,简单的逻辑调用关系是很容易看明白的。但还有那部分你可能不知道的数学逻辑时,就很难看懂了。比如:扰动函数、负载因子、斐波那契(Fibonacci)等,这些知识点的学习都需要对数学知识进行验证,否则也就学个概念,背个理论。</p>\n<p>书到用时方恨少,在下还是个宝宝!</p>\n<p>那如果你想深入的学习下程序员应该会的数学,推荐给你一位科技博主 Jeremy Kun 花了4年时间,写成一本书 <strong>《程序员数学入门》</strong>。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-6.png\" alt=\" Jeremy Kun,《程序员数学入门》\"></p>\n<p>这本书为程序员提供了大量精简后数学知识,包括:多项式、集合、图论、群论、微积分和线性代数等。同时在wiki部分还包括了抽象代数、离散数学、傅里叶分析和拓扑学等。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-7.png\" alt=\"《程序员数学入门》书中插图\"></p>\n<p>作者表示,如果你本科学过一些数学知识,那么本书还是挺适合你的,不会有什么难度。书中的前三章是基础数学内容,往后的难度依次递增。</p>\n<ul>\n<li>书籍获取:关注公众号:bugstack虫洞栈,回复:<code>程序员数学</code>,下载这本书</li>\n<li>在线Wiki:<a href=\"https://jeremykun.com/primers/\">https://jeremykun.com/primers/</a></li>\n</ul>\n<h2 id=\"六、总结\"><a href=\"#六、总结\" class=\"headerlink\" title=\"六、总结\"></a>六、总结</h2><ul>\n<li>Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. <a href=\"https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html\">https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html</a></li>\n<li>单纯的只会数学写不了代码,能写代码的不懂数学只能是CRUD码农。数学知识帮助你设计数据结构和实现算法逻辑,代码能力帮你驾驭设计模式和架构模型。多方面的知识结合和使用才是码农和工程师的主要区别,也是是否拥有核心竞争力的关键点。</li>\n<li>学习知识有时候看不到前面的路有多远,但哪怕是个泥坑,只要你不停的蠕动、折腾、翻滚,也能抓出一条泥鳅。<code>知识的路上是发现知识的快乐,还学会知识的成就感,不断的促使你前行</code>。</li>\n</ul>\n<h2 id=\"七、系列推荐\"><a href=\"#七、系列推荐\" class=\"headerlink\" title=\"七、系列推荐\"></a>七、系列推荐</h2><ul>\n<li><a href=\"https://bugstack.cn/itstack-code-life/2021/01/10/%E6%8F%A1%E8%8D%89-%E8%BF%99%E4%BA%9B%E7%A0%94%E5%8F%91%E4%BA%8B%E6%95%8530-%E6%88%91%E9%83%BD%E5%B9%B2%E8%BF%87.html\">互联网大厂,线上研发事故总结!</a></li>\n<li><a href=\"https://bugstack.cn/itstack-code-life/2020/12/13/%E7%A0%81%E5%BE%B7%E9%9C%80%E6%B1%82-%E8%BF%99%E4%B8%8D%E5%B0%B1%E6%98%AF%E4%BA%A7%E5%93%81%E7%BB%99%E6%88%91%E7%95%99%E7%9A%84%E6%95%B0%E5%AD%A6%E4%BD%9C%E4%B8%9A.html\">码德,这不就是产品给我留的数学作业!</a></li>\n<li><a href=\"https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html\">HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html\">Netty实战,1比1仿桌面版微信聊天</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html\">重学 Java 设计模式,全网最火的设计模式 17万+下载</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>数学离程序员有多近?</code></p>\n<p>ifelse也好、for循环也罢,代码可以说就是对<strong>数学逻辑的具体实现</strong>。所以敲代码的程序员几乎就离不开数学,难易不同而已。</p>\n<p>那数学不好就写不了代码吗😳?不,一样可以写代码,可以写出更多的<code>CRUD</code>出来。那你不要总觉得是产品需求简单所以你的实现过程才变成了增删改查,往往也是因为你还不具备可扩展、易维护、高性能的代码实现方案落地能力,才使得你小小年纪写出了更多的<code>CRUD</code>!</p>","more":"<p>与一锥子买卖的小作坊相比,大厂和超级大厂更会注重数学能力。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-1.png\" alt=\"first 10-digit prime found in consecutive digits of e\"></p>\n<p><strong>2004年</strong>,在硅谷的交通动脉 101 公路上突然出现一块巨大的广告牌,上面是一道数学题:<code> {e 的连续数字中最先出现的 10 位质数}</code>.com。</p>\n<p>广告:这里的 e 是数学常数,自然对数的底数,无限不循环小数。这道题的意思就是,找出 e 中最先出现的 10 位质数,然后可以得出一个网址。进入这个网址会看到 Google 为你出的第二道数学题,成功解锁这步 Google 会告诉你,<code>我们或许是”志同道合“的人</code>,你可以将简历发到这个邮箱,我们一起做点改变世界的事情。</p>\n<p><em>计算 e 值可以通过泰勒公式推导出来:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推导计算过程还包括<code>埃拉托色尼筛选法(the Sieve of Eratosthenes)</code>、<code>线性筛选法</code>的使用。感兴趣的小伙伴可以用代码实现下。</em></p>\n<h2 id=\"二、把代码写好的四步\"><a href=\"#二、把代码写好的四步\" class=\"headerlink\" title=\"二、把代码写好的四步\"></a>二、把代码写好的四步</h2><p><code>业务提需求、产品定方案、研发做实现。</code>最终这个系统开发的怎么样是由三方共同决定的!</p>\n<ul>\n<li>地基挖的不好,楼就盖不高</li>\n<li>砖头摆放不巧,楼就容易倒</li>\n<li>水电走线不妙,楼就危险了</li>\n<li>格局设计不行,楼就卖不掉</li>\n</ul>\n<p>这里的地基、砖头、水电、格局,对应的就是,数据结构、算法逻辑、设计模式、系统架构。从下到上相互依赖、相互配合,只有这一层做好,下一层才好做!</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-2.png\" alt=\"图 20-2 代码实现过程分层\"></p>\n<ul>\n<li><strong>数据结构</strong>:高矮胖瘦、长宽扁细,数据的存放方式,是一套程序开发的核心基础。不合理的设计往往是从数据结构开始,哪怕你仅仅是使用数据库存放业务信息,也一样会影响到将来各类数据的查询、汇总等实现逻辑的难易。</li>\n<li><strong>算法逻辑</strong>:是对数据结构的使用,合适的数据结构会让算法实现过程降低时间复杂度。可能你现在的多层for循环在合适的算法过程下,能被优化为更简单的方式获取数据。<em>注意:算法逻辑实现,并不一定就是排序、归并,还有你实际业务的处理流程。</em></li>\n<li><strong>设计模式</strong>:可以这么说,不使用设计模式你一样能写代码。但你愿意看到满屏幕的ifelse判断调用,还是喜欢像膏药一样的代码,粘贴来复制去?那么设计模式这套通用场景的解决方案,就是为你剔除掉代码实现过程中的恶心部分,让整套程序更加易维护、易扩展。<em>就是开发完一个月,你看它你还认识!</em></li>\n<li><strong>系统架构</strong>:描述的是三层MVC,还是四层DDD。我对这个的理解就是家里的三居还是四局格局,MVC是我们经常用的大家都熟悉,DDD无非就是家里多了个书房,把各自属于哪一个屋子的摆件规整到各自屋子里。<em>那么乱放是什么效果呢,就是自动洗屁屁马桶🚽给按到厨房了,再贵也格楞子!</em> 好,那么我们在延展下,如果你的卫生间没有流出下水道咋办?是不这个地方的数据结构就是设计缺失的,而到后面再想扩展就难了吧!</li>\n</ul>\n<p>所以,研发在承接业务需求、实现产品方案的时候。压根就不只是在一个房子的三居或者四居格局里,开始随意码砖。</p>\n<p>没有合理的数据结构、没有优化的算法逻辑、没有运用的设计模式,最终都会影响到整个系统架构变得臃肿不堪,调用混乱。在以后附加、迭代、新增的需求下,会让整个系统问题不断的放大,当你想用重构时,就有着千丝万缕般调用关系。 <em>重构就不如重写了!</em></p>\n<h2 id=\"三、for循环没算法快\"><a href=\"#三、for循环没算法快\" class=\"headerlink\" title=\"三、for循环没算法快\"></a>三、for循环没算法快</h2><p>在《编程之美》一书中,有这样一道题。求:1<del>n中,1出现的次数。比如:1</del>10,1出现了两次。</p>\n<h3 id=\"1-for-循环实现\"><a href=\"#1-for-循环实现\" class=\"headerlink\" title=\"1. for 循环实现\"></a>1. for 循环实现</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"><span class=\"keyword\">int</span> count = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">1</span>; i <= <span class=\"number\">10000000</span>; i++) {</span><br><span class=\"line\"> String str = String.valueOf(i);</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = <span class=\"number\">0</span>; j < str.length(); j++) {</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (str.charAt(j) == <span class=\"number\">49</span>) {</span><br><span class=\"line\"> count++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br><span class=\"line\">System.out.println(<span class=\"string\">"1的个数:"</span> + count);</span><br><span class=\"line\">System.out.println(<span class=\"string\">"计算耗时:"</span> + (System.currentTimeMillis() - startTime) + <span class=\"string\">"毫秒"</span>);</span><br></pre></td></tr></table></figure>\n<p>使用 for 循环的实现过程很好理解,就是往死了循环。之后把循环到的数字按照字符串拆解,判断每一位是不是数字,是就+1。这个过程很简单,但是时间复杂很高。</p>\n<h3 id=\"2-算法逻辑实现\"><a href=\"#2-算法逻辑实现\" class=\"headerlink\" title=\"2. 算法逻辑实现\"></a>2. 算法逻辑实现</h3><p><img src=\"https://bugstack.cn/assets/images/2020/all-20-3.png\" alt=\"图 20-3 1的个数循环规则\"></p>\n<p>如图 20-3 所示,其实我们能发现这个1的个数在100、1000、10000中是有规则的循环出现的。11、12、13、14或者21、31、41、51,以及单个的1出现。最终可以得出通用公式:<code>abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...</code>,abcd代表位数。另外在实现的过程还需要考虑比如不足100等情况,例如98、1232等。</p>\n<p><strong>实现过程</strong></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"><span class=\"keyword\">int</span> num = <span class=\"number\">10000000</span>, saveNum = <span class=\"number\">1</span>, countNum = <span class=\"number\">0</span>, lastNum = <span class=\"number\">0</span>;</span><br><span class=\"line\"><span class=\"keyword\">int</span> copyNum = num;</span><br><span class=\"line\"><span class=\"keyword\">while</span> (num != <span class=\"number\">0</span>) {</span><br><span class=\"line\"> lastNum = num % <span class=\"number\">10</span>;</span><br><span class=\"line\"> num /= <span class=\"number\">10</span>;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (lastNum == <span class=\"number\">0</span>) {</span><br><span class=\"line\"> <span class=\"comment\">// 如果是0那么正好是少了一次所以num不加1了</span></span><br><span class=\"line\"> countNum += num * saveNum;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> (lastNum == <span class=\"number\">1</span>) {</span><br><span class=\"line\"> <span class=\"comment\">// 如果是1说明当前数内少了一次所以num不加1,而且当前1所在位置</span></span><br><span class=\"line\"> <span class=\"comment\">// 有1的个数,就是去除当前1最高位,剩下位数,的个数。</span></span><br><span class=\"line\"> countNum += num * saveNum + copyNum % saveNum + <span class=\"number\">1</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> <span class=\"comment\">// 如果非1非0.直接用公式计算</span></span><br><span class=\"line\"> <span class=\"comment\">// abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...</span></span><br><span class=\"line\"> countNum += (num + <span class=\"number\">1</span>) * saveNum;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> saveNum *= <span class=\"number\">10</span>;</span><br><span class=\"line\">}</span><br><span class=\"line\">System.out.println(<span class=\"string\">"1的个数:"</span> + countNum);</span><br><span class=\"line\">System.out.println(<span class=\"string\">"计算耗时:"</span> + (System.currentTimeMillis() - startTime) + <span class=\"string\">"毫秒"</span>);</span><br></pre></td></tr></table></figure>\n<p>在《编程之美》一书中还不只这一种算法,感兴趣的小伙伴可以查阅。<em>但自己折腾实现后的兴奋感更强哦!</em></p>\n<h3 id=\"3-耗时曲线对比\"><a href=\"#3-耗时曲线对比\" class=\"headerlink\" title=\"3. 耗时曲线对比\"></a>3. 耗时曲线对比</h3><p>按照两种不同方式的实现逻辑,我们来计算1000、10000、10000到一个亿,求1出现的次数,看看两种方式的耗时曲线。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-4.png\" alt=\"图 20-4 耗时曲线对比\"></p>\n<ul>\n<li>for循环随着数量的不断增大后,已经趋近于无法使用了。</li>\n<li>算法逻辑依靠的计算公式,所以无论增加多少基本都会在1~2毫秒内计算完成。</li>\n</ul>\n<p><strong>那么</strong>,你的代码中是否也有类似的地方。如果使用算法逻辑配合适合的数据结构,是否可以替代一些for循环的计算方式,来使整个实现过程的时间复杂度降低。</p>\n<h2 id=\"四、Java中的算法运用\"><a href=\"#四、Java中的算法运用\" class=\"headerlink\" title=\"四、Java中的算法运用\"></a>四、Java中的算法运用</h2><p>在 Java 的 JDK 实现中有很多数学知识的运用,包括数组、链表、红黑树的数据结构以及相应的实现类ArrayList、Linkedlist、HashMap等。当你深入的了解这些类的实现后,会发现它们其实就是使用代码来实现数学逻辑而已。<em>就像你使用数学公式来计算数学题一样</em></p>\n<p>接下来小傅哥就给你介绍几个隐藏在我们代码中的数学知识。</p>\n<h3 id=\"1-HashMap的扰动函数\"><a href=\"#1-HashMap的扰动函数\" class=\"headerlink\" title=\"1. HashMap的扰动函数\"></a>1. HashMap的扰动函数</h3><p><strong>未使用扰动函数</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-4-03.png\" alt=\"未使用扰动函数,数据分布\"></p>\n<p><strong>已使用扰动函数</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-4-04.png\" alt=\"未使用扰动函数,数据分布\"></p>\n<p><strong>扰动函数公式</strong></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">static</span> <span class=\"keyword\">final</span> <span class=\"keyword\">int</span> <span class=\"title\">hash</span><span class=\"params\">(Object key)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span> h;</span><br><span class=\"line\"> <span class=\"keyword\">return</span> (key == <span class=\"keyword\">null</span>) ? <span class=\"number\">0</span> : (h = key.hashCode()) ^ (h >>> <span class=\"number\">16</span>);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li><strong>描述</strong>:以上这段代码是HashMap中用于获取hash值的扰动函数实现代码。<em>HashMap通过哈希值与桶定位坐标</em> 那么直接获取哈希值就好了,这里为什么要做一次扰动呢?</li>\n<li><strong>作用</strong>:为了证明扰动函数的作用,这里选取了10万单词计算哈希值分布在128个格子里。之后把这128个格子中的数据做图表展示。从实现数据可以看到,在使用扰动函数后,曲线更加平稳了。那么,也就是扰动后哈希碰撞会更小。</li>\n<li><strong>用途</strong>:当你有需要把数据散列分散到不同格子或者空间时,又不希望有太严重的碰撞,那么使用扰动函数就非常有必要了。比如你做的一个数据库路由,在分库分表时也是尽可能的要做到散列的。</li>\n</ul>\n<h3 id=\"2-斐波那契(Fibonacci)散列法\"><a href=\"#2-斐波那契(Fibonacci)散列法\" class=\"headerlink\" title=\"2. 斐波那契(Fibonacci)散列法\"></a>2. 斐波那契(Fibonacci)散列法</h3><p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-13-02.png\" alt=\"ThreadLocal 中 斐波那契(Fibonacci)散列法\"></p>\n<ul>\n<li><strong>描述</strong>:在 ThreadLocal 类中的数据存放,使用的是斐波那契(Fibonacci)散列法 + 开放寻址。之所以使用斐波那契数列,是为了让数据更加散列,减少哈希碰撞。具体来自数学公式的计算求值,<strong>公式</strong>:<code>f(k) = ((k * 2654435769) >> X) << Y对于常见的32位整数而言,也就是 f(k) = (k * 2654435769) >> 28</code></li>\n<li><strong>作用</strong>:与 HashMap 相比,ThreadLocal的数据结构只有数组,并没有链表和红黑树部分。而且经过我们测试验证,斐波那契散列的效果更好,也更适合 ThreadLocal。</li>\n<li><strong>用途</strong>:如果你的代码逻辑中需要存储类似 ThreadLocal 的数据结构,又不想有严重哈希碰撞,那么就可以使用 斐波那契(Fibonacci)散列法。其实除此之外还有,<code>除法散列法</code>、<code>平方散列法</code>、<code>随机数法</code>等。</li>\n</ul>\n<h3 id=\"3-梅森旋转算法(Mersenne-twister)\"><a href=\"#3-梅森旋转算法(Mersenne-twister)\" class=\"headerlink\" title=\"3. 梅森旋转算法(Mersenne twister)\"></a>3. 梅森旋转算法(Mersenne twister)</h3><p><img src=\"https://bugstack.cn/assets/images/2020/all-20-5.png\" alt=\"梅森旋转算法的三个阶段,来自CSDN博客网图\"></p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// Initializes mt[N] with a simple integer seed. This method is</span></span><br><span class=\"line\"><span class=\"comment\">// required as part of the Mersenne Twister algorithm but need</span></span><br><span class=\"line\"><span class=\"comment\">// not be made public.</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">private</span> <span class=\"keyword\">final</span> <span class=\"keyword\">void</span> <span class=\"title\">setSeed</span><span class=\"params\">(<span class=\"keyword\">int</span> seed)</span> </span>{</span><br><span class=\"line\"> <span class=\"comment\">// Annoying runtime check for initialisation of internal data</span></span><br><span class=\"line\"> <span class=\"comment\">// caused by java.util.Random invoking setSeed() during init.</span></span><br><span class=\"line\"> <span class=\"comment\">// This is unavoidable because no fields in our instance will</span></span><br><span class=\"line\"> <span class=\"comment\">// have been initialised at this point, not even if the code</span></span><br><span class=\"line\"> <span class=\"comment\">// were placed at the declaration of the member variable.</span></span><br><span class=\"line\"> <span class=\"keyword\">if</span> (mt == <span class=\"keyword\">null</span>) mt = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[N];</span><br><span class=\"line\"> <span class=\"comment\">// ---- Begin Mersenne Twister Algorithm ----</span></span><br><span class=\"line\"> mt[<span class=\"number\">0</span>] = seed;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (mti = <span class=\"number\">1</span>; mti < N; mti++) {</span><br><span class=\"line\"> mt[mti] = (MAGIC_FACTOR1 * (mt[mti-<span class=\"number\">1</span>] <span class=\"number\">6</span> (mt[mti-<span class=\"number\">1</span>] >>> <span class=\"number\">30</span>)) + mti);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> <span class=\"comment\">// ---- End Mersenne Twister Algorithm ----</span></span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>梅森旋转算法(Mersenne twister)是一个伪随机数发生算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性递归。可以快速产生高质量的伪随机数,修正了古典随机数发生算法的很多缺陷。 最为广泛使用Mersenne Twister的一种变体是MT19937,可以产生32位整数序列。</p>\n</blockquote>\n<ul>\n<li><strong>描述</strong>:梅森旋转算法分为三个阶段,获得基础的梅森旋转链、对于旋转链进行旋转算法、对于旋转算法所得的结果进行处理。</li>\n<li><strong>用途</strong>:梅森旋转算法是R、Python、Ruby、IDL、Free Pascal、PHP、Maple、Matlab、GNU多重精度运算库和GSL的默认伪随机数产生器。从C++11开始,C++也可以使用这种算法。在Boost C++,Glib和NAG数值库中,作为插件提供。</li>\n</ul>\n<h2 id=\"五、程序员数学入门\"><a href=\"#五、程序员数学入门\" class=\"headerlink\" title=\"五、程序员数学入门\"></a>五、程序员数学入门</h2><p>与接触到一个有难度的知识点学起来辛苦相比,是自己不知道自己不会什么!<em>就像上学时候老师说,你不会的就问我。我不会啥?我从哪问?一样一样的!</em></p>\n<p>代码是对数学逻辑的实现,简单的逻辑调用关系是很容易看明白的。但还有那部分你可能不知道的数学逻辑时,就很难看懂了。比如:扰动函数、负载因子、斐波那契(Fibonacci)等,这些知识点的学习都需要对数学知识进行验证,否则也就学个概念,背个理论。</p>\n<p>书到用时方恨少,在下还是个宝宝!</p>\n<p>那如果你想深入的学习下程序员应该会的数学,推荐给你一位科技博主 Jeremy Kun 花了4年时间,写成一本书 <strong>《程序员数学入门》</strong>。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-6.png\" alt=\" Jeremy Kun,《程序员数学入门》\"></p>\n<p>这本书为程序员提供了大量精简后数学知识,包括:多项式、集合、图论、群论、微积分和线性代数等。同时在wiki部分还包括了抽象代数、离散数学、傅里叶分析和拓扑学等。</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/all-20-7.png\" alt=\"《程序员数学入门》书中插图\"></p>\n<p>作者表示,如果你本科学过一些数学知识,那么本书还是挺适合你的,不会有什么难度。书中的前三章是基础数学内容,往后的难度依次递增。</p>\n<ul>\n<li>书籍获取:关注公众号:bugstack虫洞栈,回复:<code>程序员数学</code>,下载这本书</li>\n<li>在线Wiki:<a href=\"https://jeremykun.com/primers/\">https://jeremykun.com/primers/</a></li>\n</ul>\n<h2 id=\"六、总结\"><a href=\"#六、总结\" class=\"headerlink\" title=\"六、总结\"></a>六、总结</h2><ul>\n<li>Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. <a href=\"https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html\">https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html</a></li>\n<li>单纯的只会数学写不了代码,能写代码的不懂数学只能是CRUD码农。数学知识帮助你设计数据结构和实现算法逻辑,代码能力帮你驾驭设计模式和架构模型。多方面的知识结合和使用才是码农和工程师的主要区别,也是是否拥有核心竞争力的关键点。</li>\n<li>学习知识有时候看不到前面的路有多远,但哪怕是个泥坑,只要你不停的蠕动、折腾、翻滚,也能抓出一条泥鳅。<code>知识的路上是发现知识的快乐,还学会知识的成就感,不断的促使你前行</code>。</li>\n</ul>\n<h2 id=\"七、系列推荐\"><a href=\"#七、系列推荐\" class=\"headerlink\" title=\"七、系列推荐\"></a>七、系列推荐</h2><ul>\n<li><a href=\"https://bugstack.cn/itstack-code-life/2021/01/10/%E6%8F%A1%E8%8D%89-%E8%BF%99%E4%BA%9B%E7%A0%94%E5%8F%91%E4%BA%8B%E6%95%8530-%E6%88%91%E9%83%BD%E5%B9%B2%E8%BF%87.html\">互联网大厂,线上研发事故总结!</a></li>\n<li><a href=\"https://bugstack.cn/itstack-code-life/2020/12/13/%E7%A0%81%E5%BE%B7%E9%9C%80%E6%B1%82-%E8%BF%99%E4%B8%8D%E5%B0%B1%E6%98%AF%E4%BA%A7%E5%93%81%E7%BB%99%E6%88%91%E7%95%99%E7%9A%84%E6%95%B0%E5%AD%A6%E4%BD%9C%E4%B8%9A.html\">码德,这不就是产品给我留的数学作业!</a></li>\n<li><a href=\"https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html\">HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html\">Netty实战,1比1仿桌面版微信聊天</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html\">重学 Java 设计模式,全网最火的设计模式 17万+下载</a></li>\n</ul>"},{"layout":"post","title":"握草,你竟然在代码里下毒!","date":"2020-09-06T14:24:00.000Z","comments":1,"_content":"\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`学过的代码记不住?方式不对才记不住,你这么记!`\n\n- **Git**:上厕所不叫上厕所,叫拉分支!\n- **Socket**:厕所就是服务器,坑就是端口!\n- **队列**:上厕所🚽叫入队列,先进先出!\n- **栈**:去厨房🥣叫进栈,后进先出!\n- **架构**:三居的格局叫MVC,四居的格局叫DDD!\n- **理论**:系统结构设计定的好,有点bug没问题,能改。这就是茅坑跟坐便的区别。\n\n*除了有点味道以外,这回是不记住了*,我们编程写代码的过程和我们日常生活的例子,往往都是这样可以对应上,有了真实可以触及的实物,再去了解编程就会更加容易,也很难忘记。*但可能会写着写着代码,就傻笑起来!*\n\n除了这些正能量学习的例子,我们接下来再看看哪些有毒的代码!\n\n<!-- more -->\n\n## 二、代码有毒!\n\n`以下代码用好了升职加薪,用不好开除走人!`\n\n### 1. 方法命名\n\n```java\npublic List<UserInfo> queryBitchUserInfo(String req) {\n\n return null;\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:小哥应该是想写批量查询用户的方法名,结果把`batch`(*批量*),写成了`bitch`(*婊子*)\n- 点评:接口是上午写的,人是下午走的!\n\n### 2. 最佳排序\n\n```java\npublic static void main(String[] args) {\n int[] numbers = new int[]{2, 30000000, 1, 6, 40000000, 5};\n for (final int number : numbers) {\n new Thread(new Runnable() {\n @Override\n public void run() {\n try {\n Thread.sleep(number);\n System.out.println(number);\n } catch (InterruptedException ignore) {\n }\n }\n }).start();\n }\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:用数字休眠时常排序,谁醒来的时间早,谁就先输出。\n- 点评:思路清奇,要不是这次排序等了一天,老板也不能踢他!\n\n### 3. 有点烧脑\n\n```java\n@Test\npublic void test_idx_hashMap() {\n Map<String, String> map = new HashMap<>(64);\n map.put(\"alderney\", \"未实现服务\");\n map.put(\"luminance\", \"未实现服务\");\n map.put(\"chorology\", \"未实现服务\");\n map.put(\"carline\", \"未实现服务\");\n map.put(\"fluorosis\", \"未实现服务\");\n map.put(\"angora\", \"未实现服务\");\n map.put(\"insititious\", \"未实现服务\");\n map.put(\"insincere\", \"已实现服务\");\n \n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 100000000; i++) {\n map.get(\"insincere\");\n }\n System.out.println(\"耗时(initialCapacity):\" + (System.currentTimeMillis() - startTime));\n}\n```\n\n***\n\n- 指数:⭐⭐⭐⭐⭐\n- 解毒:这是一个定义`HashMap`存放业务实现key,通过key调用服务的功能。但这里的`key`,只有`insincere`有用,其他的都是未实现服务。那你看到有啥问题了吗?\n\t- 这点代码乍一看没什么问题,看明白了就是代码里下砒霜!**它的目的就一个,要让所有的key成一个链表放到HashMap中,而且把有用的key放到链表的最后,增加get时的耗时!**\n\t- 首先,`new HashMap<>(64);`为啥默认初始化64个长度?因为默认长度是8,插入元素时,当链表长度为8时候会进行扩容和链表树化判断,此时就会把原有的key散列了,不能让所有key构成一个时间复杂度较高的链表。\n\t- 其次,所有的 `key` 都是刻意选出来的,因为他们在 `HashMap` 计算下标时,下标值都为0,idx = `(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16))`,这样就能让所有 `key` 都散列到同一个位置进行碰撞。*而且单词 `insincere` 的意思是;`不诚恳的、不真诚的`!*\n\t- 最后,前7个key其实都是废 `key`,不起任何作用,只有最后一个 key 有服务。那么这样就可以在HashMap中建出来很多这样耗时的碰撞链表,当然要满足`0.75`的负载因子,不要让HashMap扩容。\n\t- 整体的效果如下图,key并没有均匀散列;\n\t \n\t\n- 点评:能写出这种代码就是薪资没给够,等着代码优化提加薪呢!\n\n### 4. 迷之求和\n\n```java\n@Test\npublic void test_add(){\n int num = 0;\n for (int i = 0; i < 100; i++) {\n num = num++;\n }\n System.out.println(num);\n}\n```\n\n***\n\n- 指数:⭐⭐\n- 解毒:最终 `num` 结果为 0,`num++` 根本没起啥作用。因为后++,是先用结果,在++操作,不会给赋值。正确写法是:num = ++ num;\n- 点评:这种错误就跟开车闯红灯似的,轻则扣分罚款,重则倾家荡产。\n\n### 5. 花里胡哨\n\n```java\nprivate boolean checkAge(int age ) {\n boolean result;\n if (age >18) \n {\n result=true;\n } else {\n result=false;\n }\n \n \n return result;\n}\n```\n\n***\n\n- 指数:⭐\n- 解毒:代码可以运行,但是可以优化为`return age > 18`。\n- 点评:你们公司是按照代码行数打绩效?不做格式化、不整洁、不看IDEA工具提示,代码是写给人看的!啥有不是!\n\n### 6. 数字判断\n\n```java\npublic boolean isNumber(String str) {\n try {\n Integer.parseInt(str);\n return true;\n } catch (Exception e) {\n return false;\n }\n}\n```\n\n***\n\n- 指数:⭐⭐\n- 解毒:判断是不是数字,不抛异常就是,抛异常就不是。这可以使用 `StringUtils` 工具包判断,也可以自己写正则判断。\n- 点评:这代码真烧,用异常做业务。这不是把🍄蘑菇给狗狗吃吗!🐕狗狗没死你到是吃蘑菇呀,你吃狗粑粑。\n\n### 7. 代码健壮\n\n```java\npublic void neverStop(){\n //一直循环\n while (true) {\n try {\n //业务处理流程\n } catch (Exception e) {\n //抓到异常,不处理、不打日志、就是不要停,继续跑\n continue ;\n }\n }\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:把可能抛异常的代码用tryCatch包起来,一直跑,遇到异常也要跑。这个时候遇到异常,要做一些流程处理,最起码要打日志和报警。\n- 点评:业务开发很多时候都是为了解决异常流程,就像`擦屁屁的纸80%的面积是保护手的。怎么滴,我看你这代码,是非要一直抠破呀!`\n\n### 8. 性能优化\n\n```java\n// APP首页查询,优化前\npublic void queryInitInfo(){\n Thread.sleep(3000);\n}\n\n// APP首页查询,优化后\npublic void queryInitInfo(){\n Thread.sleep(500);\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:没啥解毒的,一公斤鹤顶红兑了一口口水!\n- 点评:点评不了啦,抓到就开了吧!\n\n### 9. 无用日志\n\n```java\n// 规则引擎校验\npublic boolean ruleEngine(MatterReq req) {\n try {\n // 业务流程\n } catch (Exception e) {\n logger.error(e); // 只打异常,不打入参信息\n }\n}\n```\n\n***\n\n- 指数:⭐\n- 解毒:日志里只打了异常,没有入参信息,当你的方法有大量的调用时,很难快速定位问题。\n- 点评:下次记得把`产品经理`也打日志里去,要死一起死!\n\n### 10. 耗时遍历\n\n```java\n@Test\npublic void test_LinkedList() {\n\t// 初始化100万数据\n List<Integer> list = new LinkedList<Integer>(1000000);\n \n // 遍历求和\n int sum = 0;\n for (int i = 0; i < list.size(); i++) {\n sum += list.get(i);\n }\n \n}\n```\n\n***\n\n- 指数:⭐⭐⭐⭐\n- 解毒:乍一看可能觉得没什么问题,但是这个遍历求和会非常慢。主要因为链表的数据结构,每一次`list.get(i)`都是从链表的头开始查找,与`ArrayList`不同,`LinkedList`它时间复杂度是O(n)。那如果说你不知道对方传过来的是`LinkedList`还是`ArrayList`呢,其实可以通过`list instanceof RandomAccess` 进行判断。`ArrayList` 有随机访问的实现,`LinkedList` 是没有。同时也可以使用增强的for循环或者`Iterator`进行遍历。\n- 点评: 根基不牢,地动山摇!一知半解,坑了老铁!\n\n## 三、总结\n\n*好的代码千篇一律,差的程序升值加薪!*,这些有毒的代码,淋漓尽致的展示了程序员的才华出众,同时也严重怀疑就是钱给少了!\n\n**敲黑板**:想在这编码这条路上走的更远,还是需要脚踏实地的把根基打牢。所以非常推进你阅读以下系列专栏文章,夯实基础、拓展能力、提升眼界;\n\n- Java核心突破瓶颈篇:[https://bugstack.cn/itstack/interview.html](https://bugstack.cn/itstack/interview.html)\n- 重学Java设计模式篇:[https://bugstack.cn/itstack/itstack-demo-design.html](https://bugstack.cn/itstack/itstack-demo-design.html)\n- Java架构设计学习篇:[https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html](https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html)\n\n**好!**,本篇文章就到这里,有意思的代码还有很多,欢迎在评论区留下你的鬼畜代码!","source":"_posts/握草,你竟然在代码里下毒!.md","raw":"---\nlayout: post\ntitle: \"握草,你竟然在代码里下毒!\"\ndate: 2020-09-06 22:24\ncomments: true\ntags: \n\t- java \n\t- 屎山代码 \n---\n\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`学过的代码记不住?方式不对才记不住,你这么记!`\n\n- **Git**:上厕所不叫上厕所,叫拉分支!\n- **Socket**:厕所就是服务器,坑就是端口!\n- **队列**:上厕所🚽叫入队列,先进先出!\n- **栈**:去厨房🥣叫进栈,后进先出!\n- **架构**:三居的格局叫MVC,四居的格局叫DDD!\n- **理论**:系统结构设计定的好,有点bug没问题,能改。这就是茅坑跟坐便的区别。\n\n*除了有点味道以外,这回是不记住了*,我们编程写代码的过程和我们日常生活的例子,往往都是这样可以对应上,有了真实可以触及的实物,再去了解编程就会更加容易,也很难忘记。*但可能会写着写着代码,就傻笑起来!*\n\n除了这些正能量学习的例子,我们接下来再看看哪些有毒的代码!\n\n<!-- more -->\n\n## 二、代码有毒!\n\n`以下代码用好了升职加薪,用不好开除走人!`\n\n### 1. 方法命名\n\n```java\npublic List<UserInfo> queryBitchUserInfo(String req) {\n\n return null;\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:小哥应该是想写批量查询用户的方法名,结果把`batch`(*批量*),写成了`bitch`(*婊子*)\n- 点评:接口是上午写的,人是下午走的!\n\n### 2. 最佳排序\n\n```java\npublic static void main(String[] args) {\n int[] numbers = new int[]{2, 30000000, 1, 6, 40000000, 5};\n for (final int number : numbers) {\n new Thread(new Runnable() {\n @Override\n public void run() {\n try {\n Thread.sleep(number);\n System.out.println(number);\n } catch (InterruptedException ignore) {\n }\n }\n }).start();\n }\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:用数字休眠时常排序,谁醒来的时间早,谁就先输出。\n- 点评:思路清奇,要不是这次排序等了一天,老板也不能踢他!\n\n### 3. 有点烧脑\n\n```java\n@Test\npublic void test_idx_hashMap() {\n Map<String, String> map = new HashMap<>(64);\n map.put(\"alderney\", \"未实现服务\");\n map.put(\"luminance\", \"未实现服务\");\n map.put(\"chorology\", \"未实现服务\");\n map.put(\"carline\", \"未实现服务\");\n map.put(\"fluorosis\", \"未实现服务\");\n map.put(\"angora\", \"未实现服务\");\n map.put(\"insititious\", \"未实现服务\");\n map.put(\"insincere\", \"已实现服务\");\n \n long startTime = System.currentTimeMillis();\n for (int i = 0; i < 100000000; i++) {\n map.get(\"insincere\");\n }\n System.out.println(\"耗时(initialCapacity):\" + (System.currentTimeMillis() - startTime));\n}\n```\n\n***\n\n- 指数:⭐⭐⭐⭐⭐\n- 解毒:这是一个定义`HashMap`存放业务实现key,通过key调用服务的功能。但这里的`key`,只有`insincere`有用,其他的都是未实现服务。那你看到有啥问题了吗?\n\t- 这点代码乍一看没什么问题,看明白了就是代码里下砒霜!**它的目的就一个,要让所有的key成一个链表放到HashMap中,而且把有用的key放到链表的最后,增加get时的耗时!**\n\t- 首先,`new HashMap<>(64);`为啥默认初始化64个长度?因为默认长度是8,插入元素时,当链表长度为8时候会进行扩容和链表树化判断,此时就会把原有的key散列了,不能让所有key构成一个时间复杂度较高的链表。\n\t- 其次,所有的 `key` 都是刻意选出来的,因为他们在 `HashMap` 计算下标时,下标值都为0,idx = `(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16))`,这样就能让所有 `key` 都散列到同一个位置进行碰撞。*而且单词 `insincere` 的意思是;`不诚恳的、不真诚的`!*\n\t- 最后,前7个key其实都是废 `key`,不起任何作用,只有最后一个 key 有服务。那么这样就可以在HashMap中建出来很多这样耗时的碰撞链表,当然要满足`0.75`的负载因子,不要让HashMap扩容。\n\t- 整体的效果如下图,key并没有均匀散列;\n\t \n\t\n- 点评:能写出这种代码就是薪资没给够,等着代码优化提加薪呢!\n\n### 4. 迷之求和\n\n```java\n@Test\npublic void test_add(){\n int num = 0;\n for (int i = 0; i < 100; i++) {\n num = num++;\n }\n System.out.println(num);\n}\n```\n\n***\n\n- 指数:⭐⭐\n- 解毒:最终 `num` 结果为 0,`num++` 根本没起啥作用。因为后++,是先用结果,在++操作,不会给赋值。正确写法是:num = ++ num;\n- 点评:这种错误就跟开车闯红灯似的,轻则扣分罚款,重则倾家荡产。\n\n### 5. 花里胡哨\n\n```java\nprivate boolean checkAge(int age ) {\n boolean result;\n if (age >18) \n {\n result=true;\n } else {\n result=false;\n }\n \n \n return result;\n}\n```\n\n***\n\n- 指数:⭐\n- 解毒:代码可以运行,但是可以优化为`return age > 18`。\n- 点评:你们公司是按照代码行数打绩效?不做格式化、不整洁、不看IDEA工具提示,代码是写给人看的!啥有不是!\n\n### 6. 数字判断\n\n```java\npublic boolean isNumber(String str) {\n try {\n Integer.parseInt(str);\n return true;\n } catch (Exception e) {\n return false;\n }\n}\n```\n\n***\n\n- 指数:⭐⭐\n- 解毒:判断是不是数字,不抛异常就是,抛异常就不是。这可以使用 `StringUtils` 工具包判断,也可以自己写正则判断。\n- 点评:这代码真烧,用异常做业务。这不是把🍄蘑菇给狗狗吃吗!🐕狗狗没死你到是吃蘑菇呀,你吃狗粑粑。\n\n### 7. 代码健壮\n\n```java\npublic void neverStop(){\n //一直循环\n while (true) {\n try {\n //业务处理流程\n } catch (Exception e) {\n //抓到异常,不处理、不打日志、就是不要停,继续跑\n continue ;\n }\n }\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:把可能抛异常的代码用tryCatch包起来,一直跑,遇到异常也要跑。这个时候遇到异常,要做一些流程处理,最起码要打日志和报警。\n- 点评:业务开发很多时候都是为了解决异常流程,就像`擦屁屁的纸80%的面积是保护手的。怎么滴,我看你这代码,是非要一直抠破呀!`\n\n### 8. 性能优化\n\n```java\n// APP首页查询,优化前\npublic void queryInitInfo(){\n Thread.sleep(3000);\n}\n\n// APP首页查询,优化后\npublic void queryInitInfo(){\n Thread.sleep(500);\n}\n```\n\n***\n\n- 指数:⭐⭐⭐\n- 解毒:没啥解毒的,一公斤鹤顶红兑了一口口水!\n- 点评:点评不了啦,抓到就开了吧!\n\n### 9. 无用日志\n\n```java\n// 规则引擎校验\npublic boolean ruleEngine(MatterReq req) {\n try {\n // 业务流程\n } catch (Exception e) {\n logger.error(e); // 只打异常,不打入参信息\n }\n}\n```\n\n***\n\n- 指数:⭐\n- 解毒:日志里只打了异常,没有入参信息,当你的方法有大量的调用时,很难快速定位问题。\n- 点评:下次记得把`产品经理`也打日志里去,要死一起死!\n\n### 10. 耗时遍历\n\n```java\n@Test\npublic void test_LinkedList() {\n\t// 初始化100万数据\n List<Integer> list = new LinkedList<Integer>(1000000);\n \n // 遍历求和\n int sum = 0;\n for (int i = 0; i < list.size(); i++) {\n sum += list.get(i);\n }\n \n}\n```\n\n***\n\n- 指数:⭐⭐⭐⭐\n- 解毒:乍一看可能觉得没什么问题,但是这个遍历求和会非常慢。主要因为链表的数据结构,每一次`list.get(i)`都是从链表的头开始查找,与`ArrayList`不同,`LinkedList`它时间复杂度是O(n)。那如果说你不知道对方传过来的是`LinkedList`还是`ArrayList`呢,其实可以通过`list instanceof RandomAccess` 进行判断。`ArrayList` 有随机访问的实现,`LinkedList` 是没有。同时也可以使用增强的for循环或者`Iterator`进行遍历。\n- 点评: 根基不牢,地动山摇!一知半解,坑了老铁!\n\n## 三、总结\n\n*好的代码千篇一律,差的程序升值加薪!*,这些有毒的代码,淋漓尽致的展示了程序员的才华出众,同时也严重怀疑就是钱给少了!\n\n**敲黑板**:想在这编码这条路上走的更远,还是需要脚踏实地的把根基打牢。所以非常推进你阅读以下系列专栏文章,夯实基础、拓展能力、提升眼界;\n\n- Java核心突破瓶颈篇:[https://bugstack.cn/itstack/interview.html](https://bugstack.cn/itstack/interview.html)\n- 重学Java设计模式篇:[https://bugstack.cn/itstack/itstack-demo-design.html](https://bugstack.cn/itstack/itstack-demo-design.html)\n- Java架构设计学习篇:[https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html](https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html)\n\n**好!**,本篇文章就到这里,有意思的代码还有很多,欢迎在评论区留下你的鬼畜代码!","slug":"握草,你竟然在代码里下毒!","published":1,"updated":"2021-01-23T08:35:00.244Z","_id":"ckk9gh7am0000oj9l1oav2g4o","photos":[],"link":"","content":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>学过的代码记不住?方式不对才记不住,你这么记!</code></p>\n<ul>\n<li><strong>Git</strong>:上厕所不叫上厕所,叫拉分支!</li>\n<li><strong>Socket</strong>:厕所就是服务器,坑就是端口!</li>\n<li><strong>队列</strong>:上厕所🚽叫入队列,先进先出!</li>\n<li><strong>栈</strong>:去厨房🥣叫进栈,后进先出!</li>\n<li><strong>架构</strong>:三居的格局叫MVC,四居的格局叫DDD!</li>\n<li><strong>理论</strong>:系统结构设计定的好,有点bug没问题,能改。这就是茅坑跟坐便的区别。</li>\n</ul>\n<p><em>除了有点味道以外,这回是不记住了</em>,我们编程写代码的过程和我们日常生活的例子,往往都是这样可以对应上,有了真实可以触及的实物,再去了解编程就会更加容易,也很难忘记。<em>但可能会写着写着代码,就傻笑起来!</em></p>\n<p>除了这些正能量学习的例子,我们接下来再看看哪些有毒的代码!</p>\n<a id=\"more\"></a>\n\n<h2 id=\"二、代码有毒!\"><a href=\"#二、代码有毒!\" class=\"headerlink\" title=\"二、代码有毒!\"></a>二、代码有毒!</h2><p><code>以下代码用好了升职加薪,用不好开除走人!</code></p>\n<h3 id=\"1-方法命名\"><a href=\"#1-方法命名\" class=\"headerlink\" title=\"1. 方法命名\"></a>1. 方法命名</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> List<UserInfo> <span class=\"title\">queryBitchUserInfo</span><span class=\"params\">(String req)</span> </span>{</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">null</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:小哥应该是想写批量查询用户的方法名,结果把<code>batch</code>(<em>批量</em>),写成了<code>bitch</code>(<em>婊子</em>)</li>\n<li>点评:接口是上午写的,人是下午走的!</li>\n</ul>\n<h3 id=\"2-最佳排序\"><a href=\"#2-最佳排序\" class=\"headerlink\" title=\"2. 最佳排序\"></a>2. 最佳排序</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title\">main</span><span class=\"params\">(String[] args)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span>[] numbers = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[]{<span class=\"number\">2</span>, <span class=\"number\">30000000</span>, <span class=\"number\">1</span>, <span class=\"number\">6</span>, <span class=\"number\">40000000</span>, <span class=\"number\">5</span>};</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">final</span> <span class=\"keyword\">int</span> number : numbers) {</span><br><span class=\"line\"> <span class=\"keyword\">new</span> Thread(<span class=\"keyword\">new</span> Runnable() {</span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">run</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> Thread.sleep(number);</span><br><span class=\"line\"> System.out.println(number);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (InterruptedException ignore) {</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }).start();</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:用数字休眠时常排序,谁醒来的时间早,谁就先输出。</li>\n<li>点评:思路清奇,要不是这次排序等了一天,老板也不能踢他!</li>\n</ul>\n<h3 id=\"3-有点烧脑\"><a href=\"#3-有点烧脑\" class=\"headerlink\" title=\"3. 有点烧脑\"></a>3. 有点烧脑</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_idx_hashMap</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\"> Map<String, String> map = <span class=\"keyword\">new</span> HashMap<>(<span class=\"number\">64</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"alderney"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"luminance"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"chorology"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"carline"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"fluorosis"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"angora"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"insititious"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"insincere"</span>, <span class=\"string\">"已实现服务"</span>);</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < <span class=\"number\">100000000</span>; i++) {</span><br><span class=\"line\"> map.get(<span class=\"string\">"insincere"</span>);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"耗时(initialCapacity):"</span> + (System.currentTimeMillis() - startTime));</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li><p>指数:⭐⭐⭐⭐⭐</p>\n</li>\n<li><p>解毒:这是一个定义<code>HashMap</code>存放业务实现key,通过key调用服务的功能。但这里的<code>key</code>,只有<code>insincere</code>有用,其他的都是未实现服务。那你看到有啥问题了吗?</p>\n<ul>\n<li>这点代码乍一看没什么问题,看明白了就是代码里下砒霜!<strong>它的目的就一个,要让所有的key成一个链表放到HashMap中,而且把有用的key放到链表的最后,增加get时的耗时!</strong></li>\n<li>首先,<code>new HashMap<>(64);</code>为啥默认初始化64个长度?因为默认长度是8,插入元素时,当链表长度为8时候会进行扩容和链表树化判断,此时就会把原有的key散列了,不能让所有key构成一个时间复杂度较高的链表。</li>\n<li>其次,所有的 <code>key</code> 都是刻意选出来的,因为他们在 <code>HashMap</code> 计算下标时,下标值都为0,idx = <code>(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16))</code>,这样就能让所有 <code>key</code> 都散列到同一个位置进行碰撞。<em>而且单词 <code>insincere</code> 的意思是;<code>不诚恳的、不真诚的</code>!</em></li>\n<li>最后,前7个key其实都是废 <code>key</code>,不起任何作用,只有最后一个 key 有服务。那么这样就可以在HashMap中建出来很多这样耗时的碰撞链表,当然要满足<code>0.75</code>的负载因子,不要让HashMap扩容。</li>\n<li>整体的效果如下图,key并没有均匀散列;<br><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-00.png\"></li>\n</ul>\n</li>\n<li><p>点评:能写出这种代码就是薪资没给够,等着代码优化提加薪呢!</p>\n</li>\n</ul>\n<h3 id=\"4-迷之求和\"><a href=\"#4-迷之求和\" class=\"headerlink\" title=\"4. 迷之求和\"></a>4. 迷之求和</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_add</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span> num = <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < <span class=\"number\">100</span>; i++) {</span><br><span class=\"line\"> num = num++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println(num);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐</li>\n<li>解毒:最终 <code>num</code> 结果为 0,<code>num++</code> 根本没起啥作用。因为后++,是先用结果,在++操作,不会给赋值。正确写法是:num = ++ num;</li>\n<li>点评:这种错误就跟开车闯红灯似的,轻则扣分罚款,重则倾家荡产。</li>\n</ul>\n<h3 id=\"5-花里胡哨\"><a href=\"#5-花里胡哨\" class=\"headerlink\" title=\"5. 花里胡哨\"></a>5. 花里胡哨</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">private</span> <span class=\"keyword\">boolean</span> <span class=\"title\">checkAge</span><span class=\"params\">(<span class=\"keyword\">int</span> age )</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">boolean</span> result;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (age ><span class=\"number\">18</span>) </span><br><span class=\"line\"> {</span><br><span class=\"line\"> result=<span class=\"keyword\">true</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> result=<span class=\"keyword\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">return</span> result;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐</li>\n<li>解毒:代码可以运行,但是可以优化为<code>return age > 18</code>。</li>\n<li>点评:你们公司是按照代码行数打绩效?不做格式化、不整洁、不看IDEA工具提示,代码是写给人看的!啥有不是!</li>\n</ul>\n<h3 id=\"6-数字判断\"><a href=\"#6-数字判断\" class=\"headerlink\" title=\"6. 数字判断\"></a>6. 数字判断</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">boolean</span> <span class=\"title\">isNumber</span><span class=\"params\">(String str)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> Integer.parseInt(str);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">true</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐</li>\n<li>解毒:判断是不是数字,不抛异常就是,抛异常就不是。这可以使用 <code>StringUtils</code> 工具包判断,也可以自己写正则判断。</li>\n<li>点评:这代码真烧,用异常做业务。这不是把🍄蘑菇给狗狗吃吗!🐕狗狗没死你到是吃蘑菇呀,你吃狗粑粑。</li>\n</ul>\n<h3 id=\"7-代码健壮\"><a href=\"#7-代码健壮\" class=\"headerlink\" title=\"7. 代码健壮\"></a>7. 代码健壮</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">neverStop</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> <span class=\"comment\">//一直循环</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span> (<span class=\"keyword\">true</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"comment\">//业务处理流程</span></span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> <span class=\"comment\">//抓到异常,不处理、不打日志、就是不要停,继续跑</span></span><br><span class=\"line\"> <span class=\"keyword\">continue</span> ;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:把可能抛异常的代码用tryCatch包起来,一直跑,遇到异常也要跑。这个时候遇到异常,要做一些流程处理,最起码要打日志和报警。</li>\n<li>点评:业务开发很多时候都是为了解决异常流程,就像<code>擦屁屁的纸80%的面积是保护手的。怎么滴,我看你这代码,是非要一直抠破呀!</code></li>\n</ul>\n<h3 id=\"8-性能优化\"><a href=\"#8-性能优化\" class=\"headerlink\" title=\"8. 性能优化\"></a>8. 性能优化</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// APP首页查询,优化前</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">queryInitInfo</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> Thread.sleep(<span class=\"number\">3000</span>);</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">// APP首页查询,优化后</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">queryInitInfo</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> Thread.sleep(<span class=\"number\">500</span>);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:没啥解毒的,一公斤鹤顶红兑了一口口水!</li>\n<li>点评:点评不了啦,抓到就开了吧!</li>\n</ul>\n<h3 id=\"9-无用日志\"><a href=\"#9-无用日志\" class=\"headerlink\" title=\"9. 无用日志\"></a>9. 无用日志</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// 规则引擎校验</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">boolean</span> <span class=\"title\">ruleEngine</span><span class=\"params\">(MatterReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(e); <span class=\"comment\">// 只打异常,不打入参信息</span></span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐</li>\n<li>解毒:日志里只打了异常,没有入参信息,当你的方法有大量的调用时,很难快速定位问题。</li>\n<li>点评:下次记得把<code>产品经理</code>也打日志里去,要死一起死!</li>\n</ul>\n<h3 id=\"10-耗时遍历\"><a href=\"#10-耗时遍历\" class=\"headerlink\" title=\"10. 耗时遍历\"></a>10. 耗时遍历</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_LinkedList</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\">\t<span class=\"comment\">// 初始化100万数据</span></span><br><span class=\"line\"> List<Integer> list = <span class=\"keyword\">new</span> LinkedList<Integer>(<span class=\"number\">1000000</span>);</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">// 遍历求和</span></span><br><span class=\"line\"> <span class=\"keyword\">int</span> sum = <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < list.size(); i++) {</span><br><span class=\"line\"> sum += list.get(i);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐⭐</li>\n<li>解毒:乍一看可能觉得没什么问题,但是这个遍历求和会非常慢。主要因为链表的数据结构,每一次<code>list.get(i)</code>都是从链表的头开始查找,与<code>ArrayList</code>不同,<code>LinkedList</code>它时间复杂度是O(n)。那如果说你不知道对方传过来的是<code>LinkedList</code>还是<code>ArrayList</code>呢,其实可以通过<code>list instanceof RandomAccess</code> 进行判断。<code>ArrayList</code> 有随机访问的实现,<code>LinkedList</code> 是没有。同时也可以使用增强的for循环或者<code>Iterator</code>进行遍历。</li>\n<li>点评: 根基不牢,地动山摇!一知半解,坑了老铁!</li>\n</ul>\n<h2 id=\"三、总结\"><a href=\"#三、总结\" class=\"headerlink\" title=\"三、总结\"></a>三、总结</h2><p><em>好的代码千篇一律,差的程序升值加薪!</em>,这些有毒的代码,淋漓尽致的展示了程序员的才华出众,同时也严重怀疑就是钱给少了!</p>\n<p><strong>敲黑板</strong>:想在这编码这条路上走的更远,还是需要脚踏实地的把根基打牢。所以非常推进你阅读以下系列专栏文章,夯实基础、拓展能力、提升眼界;</p>\n<ul>\n<li>Java核心突破瓶颈篇:<a href=\"https://bugstack.cn/itstack/interview.html\">https://bugstack.cn/itstack/interview.html</a></li>\n<li>重学Java设计模式篇:<a href=\"https://bugstack.cn/itstack/itstack-demo-design.html\">https://bugstack.cn/itstack/itstack-demo-design.html</a></li>\n<li>Java架构设计学习篇:<a href=\"https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html\">https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html</a></li>\n</ul>\n<p><strong>好!</strong>,本篇文章就到这里,有意思的代码还有很多,欢迎在评论区留下你的鬼畜代码!</p>\n","site":{"data":{}},"excerpt":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>学过的代码记不住?方式不对才记不住,你这么记!</code></p>\n<ul>\n<li><strong>Git</strong>:上厕所不叫上厕所,叫拉分支!</li>\n<li><strong>Socket</strong>:厕所就是服务器,坑就是端口!</li>\n<li><strong>队列</strong>:上厕所🚽叫入队列,先进先出!</li>\n<li><strong>栈</strong>:去厨房🥣叫进栈,后进先出!</li>\n<li><strong>架构</strong>:三居的格局叫MVC,四居的格局叫DDD!</li>\n<li><strong>理论</strong>:系统结构设计定的好,有点bug没问题,能改。这就是茅坑跟坐便的区别。</li>\n</ul>\n<p><em>除了有点味道以外,这回是不记住了</em>,我们编程写代码的过程和我们日常生活的例子,往往都是这样可以对应上,有了真实可以触及的实物,再去了解编程就会更加容易,也很难忘记。<em>但可能会写着写着代码,就傻笑起来!</em></p>\n<p>除了这些正能量学习的例子,我们接下来再看看哪些有毒的代码!</p>","more":"<h2 id=\"二、代码有毒!\"><a href=\"#二、代码有毒!\" class=\"headerlink\" title=\"二、代码有毒!\"></a>二、代码有毒!</h2><p><code>以下代码用好了升职加薪,用不好开除走人!</code></p>\n<h3 id=\"1-方法命名\"><a href=\"#1-方法命名\" class=\"headerlink\" title=\"1. 方法命名\"></a>1. 方法命名</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> List<UserInfo> <span class=\"title\">queryBitchUserInfo</span><span class=\"params\">(String req)</span> </span>{</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">null</span>;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:小哥应该是想写批量查询用户的方法名,结果把<code>batch</code>(<em>批量</em>),写成了<code>bitch</code>(<em>婊子</em>)</li>\n<li>点评:接口是上午写的,人是下午走的!</li>\n</ul>\n<h3 id=\"2-最佳排序\"><a href=\"#2-最佳排序\" class=\"headerlink\" title=\"2. 最佳排序\"></a>2. 最佳排序</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> <span class=\"title\">main</span><span class=\"params\">(String[] args)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span>[] numbers = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[]{<span class=\"number\">2</span>, <span class=\"number\">30000000</span>, <span class=\"number\">1</span>, <span class=\"number\">6</span>, <span class=\"number\">40000000</span>, <span class=\"number\">5</span>};</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">final</span> <span class=\"keyword\">int</span> number : numbers) {</span><br><span class=\"line\"> <span class=\"keyword\">new</span> Thread(<span class=\"keyword\">new</span> Runnable() {</span><br><span class=\"line\"> <span class=\"meta\">@Override</span></span><br><span class=\"line\"> <span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">run</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> Thread.sleep(number);</span><br><span class=\"line\"> System.out.println(number);</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (InterruptedException ignore) {</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }).start();</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:用数字休眠时常排序,谁醒来的时间早,谁就先输出。</li>\n<li>点评:思路清奇,要不是这次排序等了一天,老板也不能踢他!</li>\n</ul>\n<h3 id=\"3-有点烧脑\"><a href=\"#3-有点烧脑\" class=\"headerlink\" title=\"3. 有点烧脑\"></a>3. 有点烧脑</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_idx_hashMap</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\"> Map<String, String> map = <span class=\"keyword\">new</span> HashMap<>(<span class=\"number\">64</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"alderney"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"luminance"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"chorology"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"carline"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"fluorosis"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"angora"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"insititious"</span>, <span class=\"string\">"未实现服务"</span>);</span><br><span class=\"line\"> map.put(<span class=\"string\">"insincere"</span>, <span class=\"string\">"已实现服务"</span>);</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">long</span> startTime = System.currentTimeMillis();</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < <span class=\"number\">100000000</span>; i++) {</span><br><span class=\"line\"> map.get(<span class=\"string\">"insincere"</span>);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println(<span class=\"string\">"耗时(initialCapacity):"</span> + (System.currentTimeMillis() - startTime));</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li><p>指数:⭐⭐⭐⭐⭐</p>\n</li>\n<li><p>解毒:这是一个定义<code>HashMap</code>存放业务实现key,通过key调用服务的功能。但这里的<code>key</code>,只有<code>insincere</code>有用,其他的都是未实现服务。那你看到有啥问题了吗?</p>\n<ul>\n<li>这点代码乍一看没什么问题,看明白了就是代码里下砒霜!<strong>它的目的就一个,要让所有的key成一个链表放到HashMap中,而且把有用的key放到链表的最后,增加get时的耗时!</strong></li>\n<li>首先,<code>new HashMap<>(64);</code>为啥默认初始化64个长度?因为默认长度是8,插入元素时,当链表长度为8时候会进行扩容和链表树化判断,此时就会把原有的key散列了,不能让所有key构成一个时间复杂度较高的链表。</li>\n<li>其次,所有的 <code>key</code> 都是刻意选出来的,因为他们在 <code>HashMap</code> 计算下标时,下标值都为0,idx = <code>(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16))</code>,这样就能让所有 <code>key</code> 都散列到同一个位置进行碰撞。<em>而且单词 <code>insincere</code> 的意思是;<code>不诚恳的、不真诚的</code>!</em></li>\n<li>最后,前7个key其实都是废 <code>key</code>,不起任何作用,只有最后一个 key 有服务。那么这样就可以在HashMap中建出来很多这样耗时的碰撞链表,当然要满足<code>0.75</code>的负载因子,不要让HashMap扩容。</li>\n<li>整体的效果如下图,key并没有均匀散列;<br><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-00.png\"></li>\n</ul>\n</li>\n<li><p>点评:能写出这种代码就是薪资没给够,等着代码优化提加薪呢!</p>\n</li>\n</ul>\n<h3 id=\"4-迷之求和\"><a href=\"#4-迷之求和\" class=\"headerlink\" title=\"4. 迷之求和\"></a>4. 迷之求和</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_add</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> <span class=\"keyword\">int</span> num = <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < <span class=\"number\">100</span>; i++) {</span><br><span class=\"line\"> num = num++;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> System.out.println(num);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐</li>\n<li>解毒:最终 <code>num</code> 结果为 0,<code>num++</code> 根本没起啥作用。因为后++,是先用结果,在++操作,不会给赋值。正确写法是:num = ++ num;</li>\n<li>点评:这种错误就跟开车闯红灯似的,轻则扣分罚款,重则倾家荡产。</li>\n</ul>\n<h3 id=\"5-花里胡哨\"><a href=\"#5-花里胡哨\" class=\"headerlink\" title=\"5. 花里胡哨\"></a>5. 花里胡哨</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">private</span> <span class=\"keyword\">boolean</span> <span class=\"title\">checkAge</span><span class=\"params\">(<span class=\"keyword\">int</span> age )</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">boolean</span> result;</span><br><span class=\"line\"> <span class=\"keyword\">if</span> (age ><span class=\"number\">18</span>) </span><br><span class=\"line\"> {</span><br><span class=\"line\"> result=<span class=\"keyword\">true</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">else</span> {</span><br><span class=\"line\"> result=<span class=\"keyword\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"keyword\">return</span> result;</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐</li>\n<li>解毒:代码可以运行,但是可以优化为<code>return age > 18</code>。</li>\n<li>点评:你们公司是按照代码行数打绩效?不做格式化、不整洁、不看IDEA工具提示,代码是写给人看的!啥有不是!</li>\n</ul>\n<h3 id=\"6-数字判断\"><a href=\"#6-数字判断\" class=\"headerlink\" title=\"6. 数字判断\"></a>6. 数字判断</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">boolean</span> <span class=\"title\">isNumber</span><span class=\"params\">(String str)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> Integer.parseInt(str);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">true</span>;</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"keyword\">false</span>;</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐</li>\n<li>解毒:判断是不是数字,不抛异常就是,抛异常就不是。这可以使用 <code>StringUtils</code> 工具包判断,也可以自己写正则判断。</li>\n<li>点评:这代码真烧,用异常做业务。这不是把🍄蘑菇给狗狗吃吗!🐕狗狗没死你到是吃蘑菇呀,你吃狗粑粑。</li>\n</ul>\n<h3 id=\"7-代码健壮\"><a href=\"#7-代码健壮\" class=\"headerlink\" title=\"7. 代码健壮\"></a>7. 代码健壮</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">neverStop</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> <span class=\"comment\">//一直循环</span></span><br><span class=\"line\"> <span class=\"keyword\">while</span> (<span class=\"keyword\">true</span>) {</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"comment\">//业务处理流程</span></span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> <span class=\"comment\">//抓到异常,不处理、不打日志、就是不要停,继续跑</span></span><br><span class=\"line\"> <span class=\"keyword\">continue</span> ;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:把可能抛异常的代码用tryCatch包起来,一直跑,遇到异常也要跑。这个时候遇到异常,要做一些流程处理,最起码要打日志和报警。</li>\n<li>点评:业务开发很多时候都是为了解决异常流程,就像<code>擦屁屁的纸80%的面积是保护手的。怎么滴,我看你这代码,是非要一直抠破呀!</code></li>\n</ul>\n<h3 id=\"8-性能优化\"><a href=\"#8-性能优化\" class=\"headerlink\" title=\"8. 性能优化\"></a>8. 性能优化</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// APP首页查询,优化前</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">queryInitInfo</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> Thread.sleep(<span class=\"number\">3000</span>);</span><br><span class=\"line\">}</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\">// APP首页查询,优化后</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">queryInitInfo</span><span class=\"params\">()</span></span>{</span><br><span class=\"line\"> Thread.sleep(<span class=\"number\">500</span>);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐</li>\n<li>解毒:没啥解毒的,一公斤鹤顶红兑了一口口水!</li>\n<li>点评:点评不了啦,抓到就开了吧!</li>\n</ul>\n<h3 id=\"9-无用日志\"><a href=\"#9-无用日志\" class=\"headerlink\" title=\"9. 无用日志\"></a>9. 无用日志</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\">// 规则引擎校验</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">boolean</span> <span class=\"title\">ruleEngine</span><span class=\"params\">(MatterReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(e); <span class=\"comment\">// 只打异常,不打入参信息</span></span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐</li>\n<li>解毒:日志里只打了异常,没有入参信息,当你的方法有大量的调用时,很难快速定位问题。</li>\n<li>点评:下次记得把<code>产品经理</code>也打日志里去,要死一起死!</li>\n</ul>\n<h3 id=\"10-耗时遍历\"><a href=\"#10-耗时遍历\" class=\"headerlink\" title=\"10. 耗时遍历\"></a>10. 耗时遍历</h3><figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">@Test</span></span><br><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> <span class=\"title\">test_LinkedList</span><span class=\"params\">()</span> </span>{</span><br><span class=\"line\">\t<span class=\"comment\">// 初始化100万数据</span></span><br><span class=\"line\"> List<Integer> list = <span class=\"keyword\">new</span> LinkedList<Integer>(<span class=\"number\">1000000</span>);</span><br><span class=\"line\"> </span><br><span class=\"line\"> <span class=\"comment\">// 遍历求和</span></span><br><span class=\"line\"> <span class=\"keyword\">int</span> sum = <span class=\"number\">0</span>;</span><br><span class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> i = <span class=\"number\">0</span>; i < list.size(); i++) {</span><br><span class=\"line\"> sum += list.get(i);</span><br><span class=\"line\"> }</span><br><span class=\"line\"> </span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<hr>\n<ul>\n<li>指数:⭐⭐⭐⭐</li>\n<li>解毒:乍一看可能觉得没什么问题,但是这个遍历求和会非常慢。主要因为链表的数据结构,每一次<code>list.get(i)</code>都是从链表的头开始查找,与<code>ArrayList</code>不同,<code>LinkedList</code>它时间复杂度是O(n)。那如果说你不知道对方传过来的是<code>LinkedList</code>还是<code>ArrayList</code>呢,其实可以通过<code>list instanceof RandomAccess</code> 进行判断。<code>ArrayList</code> 有随机访问的实现,<code>LinkedList</code> 是没有。同时也可以使用增强的for循环或者<code>Iterator</code>进行遍历。</li>\n<li>点评: 根基不牢,地动山摇!一知半解,坑了老铁!</li>\n</ul>\n<h2 id=\"三、总结\"><a href=\"#三、总结\" class=\"headerlink\" title=\"三、总结\"></a>三、总结</h2><p><em>好的代码千篇一律,差的程序升值加薪!</em>,这些有毒的代码,淋漓尽致的展示了程序员的才华出众,同时也严重怀疑就是钱给少了!</p>\n<p><strong>敲黑板</strong>:想在这编码这条路上走的更远,还是需要脚踏实地的把根基打牢。所以非常推进你阅读以下系列专栏文章,夯实基础、拓展能力、提升眼界;</p>\n<ul>\n<li>Java核心突破瓶颈篇:<a href=\"https://bugstack.cn/itstack/interview.html\">https://bugstack.cn/itstack/interview.html</a></li>\n<li>重学Java设计模式篇:<a href=\"https://bugstack.cn/itstack/itstack-demo-design.html\">https://bugstack.cn/itstack/itstack-demo-design.html</a></li>\n<li>Java架构设计学习篇:<a href=\"https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html\">https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html</a></li>\n</ul>\n<p><strong>好!</strong>,本篇文章就到这里,有意思的代码还有很多,欢迎在评论区留下你的鬼畜代码!</p>"},{"layout":"post","title":"一次代码评审,差点过不了试用期!","date":"2020-09-14T04:23:00.000Z","comments":1,"_content":"\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`好的代码往往也很好看`\n\n代码是给机器运行的,但同样也是给人看的,并且随着上线还需要由人来运维。那么写出`可扩展`、`易维护`、`好读懂`的代码就显得非常重要。\n\n对于新人来说,互联网大厂项目开发与平常自己学习的代码还是有很大的差别的。日常学习时候通常只要能运行出结果即可,并不会有其他的要求。也不会说有;PRD评审、研发设计评审、代码开发、代码评审以及中间一些列的提交物,直到测试完成,上线验证,开量对外等等。\n\n所以很多新人刚从学校毕业或者从小公司进入大厂,在规范制约下会有一些不习惯,甚至犯错误。那么为了让大家更好的知晓这些问题,小傅哥特意整理了一些例子,欢迎参考。\n\n<!-- more -->\n\n## 二、会议室\n\n`谢飞机`,刚刚入职没多久,兴奋的写着leader给的需求,🐎码的飞快。恰巧组长走过来:“飞机,带着你的电脑,跟我来码云会议室,做下代码评审。”\n\n**leader**:飞机,你这代码咋这么粗鲁!\n\n**飞机**:啊?😱\n\n**leader**:我要不拦着你,我感觉你这代码都能飞。\n\n**leader**:你看哈,就说这行,这日志打的,上线后出了问题,你能查到原因吗?\n\n**飞机**:好像...\n\n**leader**:还有这,这idea都提示你了,都报黄色了,你怎么不看看。还有,这代码也不格式化,一个月后它认识你,你还认识它吗。\n\n**leader**:给你发的入职编码规范看了?\n\n**飞机**:哦,看一些,写的时候忘了。\n\n**leader**:先别着急写,看会了再写代码,这还有一个不错的工程:[《Netty+JavaFx实战:仿桌面版微信聊天》](https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html),可以参考。\n\n`写代码不是以完成功能就算完事,还需要写的漂亮。评审后,飞机,坐回工位,收起了躁动的心,安心熟读手册并练习。`\n\n## 三、代码评审\n\n### 1. 日志规范\n\n日志是整个代码开发过程中非常重要的环节,如果日志打的不好,那么遇到的线上bug就没法快速定位,定位不了问题也就没法快速解决问题。直接带来的结果可能包括;客诉更多、资损更大、修复更慢。\n\n**就像下面这段代码中的日志**;\n\n```java\npublic Result execRule(RuleReq req) {\n try {\n logger.info(\"执行服务规则 req:{}\", JSON.toJSONString(req));\n // 业务流程\n return Result.buildSuccess();\n } catch (Exception e) {\n logger.error(\"执行服务规则失败\", e);\n return Result.buildError(e);\n }\n}\n```\n\n- 看似没什么问题,但在这段异常代码中,没有打方法的入参信息。如果方法异常时只是抛出一些异常栈信息,那么是很难定位具体的由次调用触发的。\n- 另外如果你的系统监控服务,没有类似方法跟踪ID的功能,最好还需要在日志中把本次调用具有标识性的id,作为查询条件打到日志中。\n\n**修改后的日志**:\n\n```java\npublic Result execRule(RuleReq req) {\n try {\n logger.info(\"执行服务规则{}开始 req:{}\", req.getrId(), JSON.toJSONString(req));\n // 业务流程\n logger.info(\"执行服务规则{}完成 res:{}\", req.getrId(), \"业务流程,必要的结果信息\");\n return Result.buildSuccess();\n } catch (Exception e) {\n logger.error(\"执行服务规则{}失败 req:{}\", req.getrId(), JSON.toJSONString(req), e);\n return Result.buildError(e);\n }\n}\n```\n\n- 那么现在这样改成这样打日志,就可以非常方便的查询问题,例如搜索;`执行服务规则100098921`,那么它的一整串关于这次调用的信息就可以都搜索出来了,方便排查问题。\n- 在异常中打印入参是为了更加方便的定位问题,不需要比对上下文。\n- 打日志还有很多技巧,但所有打的日志目的都为了在出问题时可以快速定位问题,但也注意不要打太多日志,精简好用即可。\n\n### 2. IDEA提示\n\n很多时候因为你,走神、疏忽、手滑,写出来的错误代码,`IntelliJ IDEA`,都会给你警告⚠提示,只是你,没有去看、没有去看、没有去看!\n\n**来自idea的警告**;\n\n\n\n- Idea在警告提示这方面非常优秀,只要你能看得见,按照它的提示修改,就可以减少很多的错误。\n- 如果你还希望有更强的提示,那么你可以按照 [`p3c`](https://github.com/alibaba/p3c) 插件,帮你检查代码错误。\n\n### 3. 代码格式\n\n可能这并不是一个致命的问题,但代码格式化最大的好处是,提升可读性、规整性、以及可以让整组人都在一个标准下执行。因为很多时候一个组的程序员,会在一个类下开发,有人格式化、有人不格式化除了不好看以外,合并代码有时候也会遇到麻烦。\n\n**不格式化的代码缺少灵魂**;\n\n\n\n- 对于严格要自己的程序员来说,代码没有格式化还是很难受的。\n- 看一段代码,只要发现差一个空格位置,都知道这是格式化还是没格式化。\n\n### 4. 单元测试\n\n单测?覆盖率?写代码不是写完就可以了吗?\n\n当然不是,你写的代码你需要保证它能你跑通你所有的流程节点,确保这份功能是没有问题的,才能提交给测试,否则来回反复,耗时耗力。这也就是写单测的目的!甚至好一点的研发可以通过单测驱动开发,在这个阶段能把一些共用的方法合并、抽离,避免过多的冗余方法。\n\n**单测长什么样**;\n\n\n\n- 单测完整基本也就是代码的健壮性更好,能把单测写好,基本提交的代码就不会有那么多测试妹子找你聊天。\n- 在很多公司中一般都会要求单测覆盖率超过多少,否则是不允许编译提交的,这有插件可以和`Jenkins`配合使用。\n\n### 5. 分支规范\n\n可能有些人看到`分支规范`根本没有感觉,因为他们开发的项目较小,没有多人开发,上线周期也短,也不会开发中添加需求。\n\n但在互联网中并不是这样,往往一个系统需要几个人维护,并同时进行开发。一般这里会包括;master分支、test分支、本次需求的分支,有这么多分支怎么用呢,如下;\n1. master分支,是主分支,也是上线分支,不允许在上面直接修改代码。\n2. test分支,是测试环境分支,每个人都需要把自己开发完的分支,提测后合并到test分支,交由测试验证。\n3. 需求分支,也是个人开发的分支,同一个需求下,大家在这个分支写代码,当然也可能这个系统模块的分支就一个人在开发。\n\n**重点**,如果有人不遵守分支规范或者压根没概念,把自己的需求代码写在test分支上,并且是多次修改提交都在test分支写。那么就危险了,严重会耽误上线;为什么?\n1. test分支,是由大家把自己的代码合并过来共用的,那么这个分支就会包含2个或者更多的并行需求,当你需要上线的时候,需要把自己的代码合并到master,但test分支代码是不能合并到master的,那么多未知的内容,根本没有在上线范围。\n2. 那么你又想上线,又不能避开test分支,就需要把你写的代码,重新粘贴过去,这个时间成本非常大。\n3. test分支,还随时有删除重新拉的可能,如果有人通知大家删除重新拉,那你的代码就会丢失。\n\n### 6. 夹带需求\n\n`提交测试,但还藏一个需求`\n\n研发开发需求代码时候,有时候会额外加一些其他代码,而且这些代码可能跟本次需求并没有关系。那为什么会这样呢?\n1. 以前留下来的bug,想修复下,但忘记告知测试\n2. 在开发这个需求时,其他产品又找过来让加功能,并说功能很小,没有发邮件通知相关测试人员\n3. 看到某块以前写的代码太乱了,就想着优化下,自信心很高,不必告诉测试\n\n那这时候你提交的代码,如果不在测试范围又出了问题,只能研发自己抗。并且在所有的研发团队,几乎是不会让夹带需求上线的,这样的做完了不算功劳,做出了问题还会被骂。\n\n所以,千万不要私自夹带!哪怕你是好心!\n\n### 7. 异常流程\n\n`擦屁屁的纸,80%的面积都是保护手的!`\n\n这句话是我经常用的,因为我们编程很多时候都是在处理异常流程,正常流程往往并不难,难的是分析出这段开发的代码有多少异常流程有没有处理。\n\n那么,会有哪些异常呢?\n1. 支付成功MQ消息发送失败,需要worker补偿\n2. PRC接口调用失败,网络超时,实际成功\n3. 接口幂等性,多次调用结果一致性\n\n等等,这些都是异常流程,尤其在一些交易提现环节,会出现各种异常,那么不可能把这些异常都反馈用户展示到界面。而是要有一些非常友好的提示,并且在服务端的流程里,有一定的补偿机制,来保证最终的调用成功,或者逆反。\n\n### 8. 代码成坨\n\n\n\n`CRUD往往可能是因为你的设计,换个人写也许不同`\n\n很多时候研发写代码,根本不考虑是否要扩展,总之一个类 + 几十行ifelse,能搞定所有需求。等下次在开发类似的,就粘贴过去再修修补补,能用就行。\n\n缺少写出良好代码的研发,一方面是经历有限,另外一方面是学了很多理论但是不好落地。比如设计模式,但自己实际写代码的时候还是很晕。\n\n这里推荐一本我写的《重学Java设计模式》,全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、包含交易、营销、秒杀、中间件、源码等22个真实场景。可以添加[小傅哥微信获取:fustack](https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html)\n\n### 9. SQL性能\n\n```sql\nselect * from table where status = 1 limit 200;\n```\n\n**这是一段定时任务扫描库表的SQL**,这段sql会定时扫库,将库表中状态是1的扫描出来进行处理,每次扫描200行。你发现有什么问题了吗?\n1. 扫描必要字段即可,不需要全部字段\n2. 这段sql会越来越慢,即使状态字段加了索引。因为`status`并不能大量排掉其他状态字段,随着数据越来越多依然是全表扫描。\n\n那么怎么优化呢,其实优化也比较简单,需要先根据状态查询到符合条件的最小的id,之后再sql的查询条件中添加`id > xx`,即可。另外如果你的任务需要多个worker扫描,增加效率,可以增加门牌号设计,提升扫描效率,如下;\n\n\n\n### 10. 结伴编程\n\n评审代码最后这点想说说,`陪伴式开发`,可能这不是结伴编程,不是共同合作,而是一个研发需要另外一个研发不断的提供帮助。有时候可能就是很简单的问题,也不想查,或者说没有意识去查,只是问。\n\n业务开发的过程,只要把流程定下来,研发设计评审完,其他的开发过程中遇到的小点并不难,只要查一查就可以搞定。当日也不是说完全不能问,只不过特别普遍,简单的代码问题,自己搞定就可以了,但这个时候还像保姆似的陪伴,就会拖累整个团队的进展,最终大家都需要扛起那个慢的。\n\n所以,如果你是那个需要陪伴的,要及早断奶,学会自己攻克,快速成长。而如果你是那个卷纸,可哪擦屁股的,要把卷纸传递给他。一个人擦一次是能力体现,反反复复擦一个人,就惹屎上身了。\n\n## 四、总结\n\n- 以上介绍了代码评审中涉及到的比较常见的点,基本也是很多研发容易忽略和犯错误的地方。这些问题点但拿出哪一个看,都不大。但运行在代码中,确都有可能发生致命或者麻烦的事情。\n- 想让自己能把代码写好,就不只面试时候造飞机的回答,什么时间复杂度、什么可重入锁、什么红黑树,什么DDD,只要你不能正确的落地和运用这些技术,说的再多都是空谈。\n- 多学一些、多看一些、多问一些,没有坏处,但要自己能成长,把吸取到的经验心得,运用到业务开发中,写出可扩展、可维护的代码,才能让自己真的升职加薪。也能让既有留下的本事,也有出去的能力。\n\n## 五、系列推荐\n\n- [握草,你竟然在代码里下毒!](https://bugstack.cn/itstack-demo-any/2020/09/06/%E6%8F%A1%E8%8D%89-%E4%BD%A0%E7%AB%9F%E7%84%B6%E5%9C%A8%E4%BB%A3%E7%A0%81%E9%87%8C%E4%B8%8B%E6%AF%92.html)\n- [DDD领域驱动设计落地方案](https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html)\n- [重学Java设计模式(22个真实开发场景)](https://bugstack.cn/itstack/itstack-demo-design.html)\n- [面经手册(上最快的车,拿最贵的offer)](https://bugstack.cn/itstack/interview.html)\n- [字节码编程(非入侵式全链路监控实践)](https://bugstack.cn/itstack/itstack-demo-bytecode.html)","source":"_posts/一次代码评审,差点过不了试用期!.md","raw":"---\nlayout: post\ntitle: \"一次代码评审,差点过不了试用期!\"\ndate: 2020-09-14 12:23\ncomments: true\ntags: \n\t- 代码评审\n\t- 设计优化\n---\n\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n> 沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n`好的代码往往也很好看`\n\n代码是给机器运行的,但同样也是给人看的,并且随着上线还需要由人来运维。那么写出`可扩展`、`易维护`、`好读懂`的代码就显得非常重要。\n\n对于新人来说,互联网大厂项目开发与平常自己学习的代码还是有很大的差别的。日常学习时候通常只要能运行出结果即可,并不会有其他的要求。也不会说有;PRD评审、研发设计评审、代码开发、代码评审以及中间一些列的提交物,直到测试完成,上线验证,开量对外等等。\n\n所以很多新人刚从学校毕业或者从小公司进入大厂,在规范制约下会有一些不习惯,甚至犯错误。那么为了让大家更好的知晓这些问题,小傅哥特意整理了一些例子,欢迎参考。\n\n<!-- more -->\n\n## 二、会议室\n\n`谢飞机`,刚刚入职没多久,兴奋的写着leader给的需求,🐎码的飞快。恰巧组长走过来:“飞机,带着你的电脑,跟我来码云会议室,做下代码评审。”\n\n**leader**:飞机,你这代码咋这么粗鲁!\n\n**飞机**:啊?😱\n\n**leader**:我要不拦着你,我感觉你这代码都能飞。\n\n**leader**:你看哈,就说这行,这日志打的,上线后出了问题,你能查到原因吗?\n\n**飞机**:好像...\n\n**leader**:还有这,这idea都提示你了,都报黄色了,你怎么不看看。还有,这代码也不格式化,一个月后它认识你,你还认识它吗。\n\n**leader**:给你发的入职编码规范看了?\n\n**飞机**:哦,看一些,写的时候忘了。\n\n**leader**:先别着急写,看会了再写代码,这还有一个不错的工程:[《Netty+JavaFx实战:仿桌面版微信聊天》](https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html),可以参考。\n\n`写代码不是以完成功能就算完事,还需要写的漂亮。评审后,飞机,坐回工位,收起了躁动的心,安心熟读手册并练习。`\n\n## 三、代码评审\n\n### 1. 日志规范\n\n日志是整个代码开发过程中非常重要的环节,如果日志打的不好,那么遇到的线上bug就没法快速定位,定位不了问题也就没法快速解决问题。直接带来的结果可能包括;客诉更多、资损更大、修复更慢。\n\n**就像下面这段代码中的日志**;\n\n```java\npublic Result execRule(RuleReq req) {\n try {\n logger.info(\"执行服务规则 req:{}\", JSON.toJSONString(req));\n // 业务流程\n return Result.buildSuccess();\n } catch (Exception e) {\n logger.error(\"执行服务规则失败\", e);\n return Result.buildError(e);\n }\n}\n```\n\n- 看似没什么问题,但在这段异常代码中,没有打方法的入参信息。如果方法异常时只是抛出一些异常栈信息,那么是很难定位具体的由次调用触发的。\n- 另外如果你的系统监控服务,没有类似方法跟踪ID的功能,最好还需要在日志中把本次调用具有标识性的id,作为查询条件打到日志中。\n\n**修改后的日志**:\n\n```java\npublic Result execRule(RuleReq req) {\n try {\n logger.info(\"执行服务规则{}开始 req:{}\", req.getrId(), JSON.toJSONString(req));\n // 业务流程\n logger.info(\"执行服务规则{}完成 res:{}\", req.getrId(), \"业务流程,必要的结果信息\");\n return Result.buildSuccess();\n } catch (Exception e) {\n logger.error(\"执行服务规则{}失败 req:{}\", req.getrId(), JSON.toJSONString(req), e);\n return Result.buildError(e);\n }\n}\n```\n\n- 那么现在这样改成这样打日志,就可以非常方便的查询问题,例如搜索;`执行服务规则100098921`,那么它的一整串关于这次调用的信息就可以都搜索出来了,方便排查问题。\n- 在异常中打印入参是为了更加方便的定位问题,不需要比对上下文。\n- 打日志还有很多技巧,但所有打的日志目的都为了在出问题时可以快速定位问题,但也注意不要打太多日志,精简好用即可。\n\n### 2. IDEA提示\n\n很多时候因为你,走神、疏忽、手滑,写出来的错误代码,`IntelliJ IDEA`,都会给你警告⚠提示,只是你,没有去看、没有去看、没有去看!\n\n**来自idea的警告**;\n\n\n\n- Idea在警告提示这方面非常优秀,只要你能看得见,按照它的提示修改,就可以减少很多的错误。\n- 如果你还希望有更强的提示,那么你可以按照 [`p3c`](https://github.com/alibaba/p3c) 插件,帮你检查代码错误。\n\n### 3. 代码格式\n\n可能这并不是一个致命的问题,但代码格式化最大的好处是,提升可读性、规整性、以及可以让整组人都在一个标准下执行。因为很多时候一个组的程序员,会在一个类下开发,有人格式化、有人不格式化除了不好看以外,合并代码有时候也会遇到麻烦。\n\n**不格式化的代码缺少灵魂**;\n\n\n\n- 对于严格要自己的程序员来说,代码没有格式化还是很难受的。\n- 看一段代码,只要发现差一个空格位置,都知道这是格式化还是没格式化。\n\n### 4. 单元测试\n\n单测?覆盖率?写代码不是写完就可以了吗?\n\n当然不是,你写的代码你需要保证它能你跑通你所有的流程节点,确保这份功能是没有问题的,才能提交给测试,否则来回反复,耗时耗力。这也就是写单测的目的!甚至好一点的研发可以通过单测驱动开发,在这个阶段能把一些共用的方法合并、抽离,避免过多的冗余方法。\n\n**单测长什么样**;\n\n\n\n- 单测完整基本也就是代码的健壮性更好,能把单测写好,基本提交的代码就不会有那么多测试妹子找你聊天。\n- 在很多公司中一般都会要求单测覆盖率超过多少,否则是不允许编译提交的,这有插件可以和`Jenkins`配合使用。\n\n### 5. 分支规范\n\n可能有些人看到`分支规范`根本没有感觉,因为他们开发的项目较小,没有多人开发,上线周期也短,也不会开发中添加需求。\n\n但在互联网中并不是这样,往往一个系统需要几个人维护,并同时进行开发。一般这里会包括;master分支、test分支、本次需求的分支,有这么多分支怎么用呢,如下;\n1. master分支,是主分支,也是上线分支,不允许在上面直接修改代码。\n2. test分支,是测试环境分支,每个人都需要把自己开发完的分支,提测后合并到test分支,交由测试验证。\n3. 需求分支,也是个人开发的分支,同一个需求下,大家在这个分支写代码,当然也可能这个系统模块的分支就一个人在开发。\n\n**重点**,如果有人不遵守分支规范或者压根没概念,把自己的需求代码写在test分支上,并且是多次修改提交都在test分支写。那么就危险了,严重会耽误上线;为什么?\n1. test分支,是由大家把自己的代码合并过来共用的,那么这个分支就会包含2个或者更多的并行需求,当你需要上线的时候,需要把自己的代码合并到master,但test分支代码是不能合并到master的,那么多未知的内容,根本没有在上线范围。\n2. 那么你又想上线,又不能避开test分支,就需要把你写的代码,重新粘贴过去,这个时间成本非常大。\n3. test分支,还随时有删除重新拉的可能,如果有人通知大家删除重新拉,那你的代码就会丢失。\n\n### 6. 夹带需求\n\n`提交测试,但还藏一个需求`\n\n研发开发需求代码时候,有时候会额外加一些其他代码,而且这些代码可能跟本次需求并没有关系。那为什么会这样呢?\n1. 以前留下来的bug,想修复下,但忘记告知测试\n2. 在开发这个需求时,其他产品又找过来让加功能,并说功能很小,没有发邮件通知相关测试人员\n3. 看到某块以前写的代码太乱了,就想着优化下,自信心很高,不必告诉测试\n\n那这时候你提交的代码,如果不在测试范围又出了问题,只能研发自己抗。并且在所有的研发团队,几乎是不会让夹带需求上线的,这样的做完了不算功劳,做出了问题还会被骂。\n\n所以,千万不要私自夹带!哪怕你是好心!\n\n### 7. 异常流程\n\n`擦屁屁的纸,80%的面积都是保护手的!`\n\n这句话是我经常用的,因为我们编程很多时候都是在处理异常流程,正常流程往往并不难,难的是分析出这段开发的代码有多少异常流程有没有处理。\n\n那么,会有哪些异常呢?\n1. 支付成功MQ消息发送失败,需要worker补偿\n2. PRC接口调用失败,网络超时,实际成功\n3. 接口幂等性,多次调用结果一致性\n\n等等,这些都是异常流程,尤其在一些交易提现环节,会出现各种异常,那么不可能把这些异常都反馈用户展示到界面。而是要有一些非常友好的提示,并且在服务端的流程里,有一定的补偿机制,来保证最终的调用成功,或者逆反。\n\n### 8. 代码成坨\n\n\n\n`CRUD往往可能是因为你的设计,换个人写也许不同`\n\n很多时候研发写代码,根本不考虑是否要扩展,总之一个类 + 几十行ifelse,能搞定所有需求。等下次在开发类似的,就粘贴过去再修修补补,能用就行。\n\n缺少写出良好代码的研发,一方面是经历有限,另外一方面是学了很多理论但是不好落地。比如设计模式,但自己实际写代码的时候还是很晕。\n\n这里推荐一本我写的《重学Java设计模式》,全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、包含交易、营销、秒杀、中间件、源码等22个真实场景。可以添加[小傅哥微信获取:fustack](https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html)\n\n### 9. SQL性能\n\n```sql\nselect * from table where status = 1 limit 200;\n```\n\n**这是一段定时任务扫描库表的SQL**,这段sql会定时扫库,将库表中状态是1的扫描出来进行处理,每次扫描200行。你发现有什么问题了吗?\n1. 扫描必要字段即可,不需要全部字段\n2. 这段sql会越来越慢,即使状态字段加了索引。因为`status`并不能大量排掉其他状态字段,随着数据越来越多依然是全表扫描。\n\n那么怎么优化呢,其实优化也比较简单,需要先根据状态查询到符合条件的最小的id,之后再sql的查询条件中添加`id > xx`,即可。另外如果你的任务需要多个worker扫描,增加效率,可以增加门牌号设计,提升扫描效率,如下;\n\n\n\n### 10. 结伴编程\n\n评审代码最后这点想说说,`陪伴式开发`,可能这不是结伴编程,不是共同合作,而是一个研发需要另外一个研发不断的提供帮助。有时候可能就是很简单的问题,也不想查,或者说没有意识去查,只是问。\n\n业务开发的过程,只要把流程定下来,研发设计评审完,其他的开发过程中遇到的小点并不难,只要查一查就可以搞定。当日也不是说完全不能问,只不过特别普遍,简单的代码问题,自己搞定就可以了,但这个时候还像保姆似的陪伴,就会拖累整个团队的进展,最终大家都需要扛起那个慢的。\n\n所以,如果你是那个需要陪伴的,要及早断奶,学会自己攻克,快速成长。而如果你是那个卷纸,可哪擦屁股的,要把卷纸传递给他。一个人擦一次是能力体现,反反复复擦一个人,就惹屎上身了。\n\n## 四、总结\n\n- 以上介绍了代码评审中涉及到的比较常见的点,基本也是很多研发容易忽略和犯错误的地方。这些问题点但拿出哪一个看,都不大。但运行在代码中,确都有可能发生致命或者麻烦的事情。\n- 想让自己能把代码写好,就不只面试时候造飞机的回答,什么时间复杂度、什么可重入锁、什么红黑树,什么DDD,只要你不能正确的落地和运用这些技术,说的再多都是空谈。\n- 多学一些、多看一些、多问一些,没有坏处,但要自己能成长,把吸取到的经验心得,运用到业务开发中,写出可扩展、可维护的代码,才能让自己真的升职加薪。也能让既有留下的本事,也有出去的能力。\n\n## 五、系列推荐\n\n- [握草,你竟然在代码里下毒!](https://bugstack.cn/itstack-demo-any/2020/09/06/%E6%8F%A1%E8%8D%89-%E4%BD%A0%E7%AB%9F%E7%84%B6%E5%9C%A8%E4%BB%A3%E7%A0%81%E9%87%8C%E4%B8%8B%E6%AF%92.html)\n- [DDD领域驱动设计落地方案](https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html)\n- [重学Java设计模式(22个真实开发场景)](https://bugstack.cn/itstack/itstack-demo-design.html)\n- [面经手册(上最快的车,拿最贵的offer)](https://bugstack.cn/itstack/interview.html)\n- [字节码编程(非入侵式全链路监控实践)](https://bugstack.cn/itstack/itstack-demo-bytecode.html)","slug":"一次代码评审,差点过不了试用期!","published":1,"updated":"2021-01-23T08:39:01.648Z","photos":[],"link":"","_id":"ckk9gom3u0000t99lan01embj","content":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>好的代码往往也很好看</code></p>\n<p>代码是给机器运行的,但同样也是给人看的,并且随着上线还需要由人来运维。那么写出<code>可扩展</code>、<code>易维护</code>、<code>好读懂</code>的代码就显得非常重要。</p>\n<p>对于新人来说,互联网大厂项目开发与平常自己学习的代码还是有很大的差别的。日常学习时候通常只要能运行出结果即可,并不会有其他的要求。也不会说有;PRD评审、研发设计评审、代码开发、代码评审以及中间一些列的提交物,直到测试完成,上线验证,开量对外等等。</p>\n<p>所以很多新人刚从学校毕业或者从小公司进入大厂,在规范制约下会有一些不习惯,甚至犯错误。那么为了让大家更好的知晓这些问题,小傅哥特意整理了一些例子,欢迎参考。</p>\n<a id=\"more\"></a>\n\n<h2 id=\"二、会议室\"><a href=\"#二、会议室\" class=\"headerlink\" title=\"二、会议室\"></a>二、会议室</h2><p><code>谢飞机</code>,刚刚入职没多久,兴奋的写着leader给的需求,🐎码的飞快。恰巧组长走过来:“飞机,带着你的电脑,跟我来码云会议室,做下代码评审。”</p>\n<p><strong>leader</strong>:飞机,你这代码咋这么粗鲁!</p>\n<p><strong>飞机</strong>:啊?😱</p>\n<p><strong>leader</strong>:我要不拦着你,我感觉你这代码都能飞。</p>\n<p><strong>leader</strong>:你看哈,就说这行,这日志打的,上线后出了问题,你能查到原因吗?</p>\n<p><strong>飞机</strong>:好像…</p>\n<p><strong>leader</strong>:还有这,这idea都提示你了,都报黄色了,你怎么不看看。还有,这代码也不格式化,一个月后它认识你,你还认识它吗。</p>\n<p><strong>leader</strong>:给你发的入职编码规范看了?</p>\n<p><strong>飞机</strong>:哦,看一些,写的时候忘了。</p>\n<p><strong>leader</strong>:先别着急写,看会了再写代码,这还有一个不错的工程:<a href=\"https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html\">《Netty+JavaFx实战:仿桌面版微信聊天》</a>,可以参考。</p>\n<p><code>写代码不是以完成功能就算完事,还需要写的漂亮。评审后,飞机,坐回工位,收起了躁动的心,安心熟读手册并练习。</code></p>\n<h2 id=\"三、代码评审\"><a href=\"#三、代码评审\" class=\"headerlink\" title=\"三、代码评审\"></a>三、代码评审</h2><h3 id=\"1-日志规范\"><a href=\"#1-日志规范\" class=\"headerlink\" title=\"1. 日志规范\"></a>1. 日志规范</h3><p>日志是整个代码开发过程中非常重要的环节,如果日志打的不好,那么遇到的线上bug就没法快速定位,定位不了问题也就没法快速解决问题。直接带来的结果可能包括;客诉更多、资损更大、修复更慢。</p>\n<p><strong>就像下面这段代码中的日志</strong>;</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> Result <span class=\"title\">execRule</span><span class=\"params\">(RuleReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则 req:{}"</span>, JSON.toJSONString(req));</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildSuccess();</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(<span class=\"string\">"执行服务规则失败"</span>, e);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildError(e);</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li>看似没什么问题,但在这段异常代码中,没有打方法的入参信息。如果方法异常时只是抛出一些异常栈信息,那么是很难定位具体的由次调用触发的。</li>\n<li>另外如果你的系统监控服务,没有类似方法跟踪ID的功能,最好还需要在日志中把本次调用具有标识性的id,作为查询条件打到日志中。</li>\n</ul>\n<p><strong>修改后的日志</strong>:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> Result <span class=\"title\">execRule</span><span class=\"params\">(RuleReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则{}开始 req:{}"</span>, req.getrId(), JSON.toJSONString(req));</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则{}完成 res:{}"</span>, req.getrId(), <span class=\"string\">"业务流程,必要的结果信息"</span>);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildSuccess();</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(<span class=\"string\">"执行服务规则{}失败 req:{}"</span>, req.getrId(), JSON.toJSONString(req), e);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildError(e);</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li>那么现在这样改成这样打日志,就可以非常方便的查询问题,例如搜索;<code>执行服务规则100098921</code>,那么它的一整串关于这次调用的信息就可以都搜索出来了,方便排查问题。</li>\n<li>在异常中打印入参是为了更加方便的定位问题,不需要比对上下文。</li>\n<li>打日志还有很多技巧,但所有打的日志目的都为了在出问题时可以快速定位问题,但也注意不要打太多日志,精简好用即可。</li>\n</ul>\n<h3 id=\"2-IDEA提示\"><a href=\"#2-IDEA提示\" class=\"headerlink\" title=\"2. IDEA提示\"></a>2. IDEA提示</h3><p>很多时候因为你,走神、疏忽、手滑,写出来的错误代码,<code>IntelliJ IDEA</code>,都会给你警告⚠提示,只是你,没有去看、没有去看、没有去看!</p>\n<p><strong>来自idea的警告</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-01.png\" alt=\"小傅哥 & idea警告\"></p>\n<ul>\n<li>Idea在警告提示这方面非常优秀,只要你能看得见,按照它的提示修改,就可以减少很多的错误。</li>\n<li>如果你还希望有更强的提示,那么你可以按照 <a href=\"https://github.com/alibaba/p3c\"><code>p3c</code></a> 插件,帮你检查代码错误。</li>\n</ul>\n<h3 id=\"3-代码格式\"><a href=\"#3-代码格式\" class=\"headerlink\" title=\"3. 代码格式\"></a>3. 代码格式</h3><p>可能这并不是一个致命的问题,但代码格式化最大的好处是,提升可读性、规整性、以及可以让整组人都在一个标准下执行。因为很多时候一个组的程序员,会在一个类下开发,有人格式化、有人不格式化除了不好看以外,合并代码有时候也会遇到麻烦。</p>\n<p><strong>不格式化的代码缺少灵魂</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-02.png\" alt=\"小傅哥 & 代码格式化\"></p>\n<ul>\n<li>对于严格要自己的程序员来说,代码没有格式化还是很难受的。</li>\n<li>看一段代码,只要发现差一个空格位置,都知道这是格式化还是没格式化。</li>\n</ul>\n<h3 id=\"4-单元测试\"><a href=\"#4-单元测试\" class=\"headerlink\" title=\"4. 单元测试\"></a>4. 单元测试</h3><p>单测?覆盖率?写代码不是写完就可以了吗?</p>\n<p>当然不是,你写的代码你需要保证它能你跑通你所有的流程节点,确保这份功能是没有问题的,才能提交给测试,否则来回反复,耗时耗力。这也就是写单测的目的!甚至好一点的研发可以通过单测驱动开发,在这个阶段能把一些共用的方法合并、抽离,避免过多的冗余方法。</p>\n<p><strong>单测长什么样</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-03.png\"></p>\n<ul>\n<li>单测完整基本也就是代码的健壮性更好,能把单测写好,基本提交的代码就不会有那么多测试妹子找你聊天。</li>\n<li>在很多公司中一般都会要求单测覆盖率超过多少,否则是不允许编译提交的,这有插件可以和<code>Jenkins</code>配合使用。</li>\n</ul>\n<h3 id=\"5-分支规范\"><a href=\"#5-分支规范\" class=\"headerlink\" title=\"5. 分支规范\"></a>5. 分支规范</h3><p>可能有些人看到<code>分支规范</code>根本没有感觉,因为他们开发的项目较小,没有多人开发,上线周期也短,也不会开发中添加需求。</p>\n<p>但在互联网中并不是这样,往往一个系统需要几个人维护,并同时进行开发。一般这里会包括;master分支、test分支、本次需求的分支,有这么多分支怎么用呢,如下;</p>\n<ol>\n<li>master分支,是主分支,也是上线分支,不允许在上面直接修改代码。</li>\n<li>test分支,是测试环境分支,每个人都需要把自己开发完的分支,提测后合并到test分支,交由测试验证。</li>\n<li>需求分支,也是个人开发的分支,同一个需求下,大家在这个分支写代码,当然也可能这个系统模块的分支就一个人在开发。</li>\n</ol>\n<p><strong>重点</strong>,如果有人不遵守分支规范或者压根没概念,把自己的需求代码写在test分支上,并且是多次修改提交都在test分支写。那么就危险了,严重会耽误上线;为什么?</p>\n<ol>\n<li>test分支,是由大家把自己的代码合并过来共用的,那么这个分支就会包含2个或者更多的并行需求,当你需要上线的时候,需要把自己的代码合并到master,但test分支代码是不能合并到master的,那么多未知的内容,根本没有在上线范围。</li>\n<li>那么你又想上线,又不能避开test分支,就需要把你写的代码,重新粘贴过去,这个时间成本非常大。</li>\n<li>test分支,还随时有删除重新拉的可能,如果有人通知大家删除重新拉,那你的代码就会丢失。</li>\n</ol>\n<h3 id=\"6-夹带需求\"><a href=\"#6-夹带需求\" class=\"headerlink\" title=\"6. 夹带需求\"></a>6. 夹带需求</h3><p><code>提交测试,但还藏一个需求</code></p>\n<p>研发开发需求代码时候,有时候会额外加一些其他代码,而且这些代码可能跟本次需求并没有关系。那为什么会这样呢?</p>\n<ol>\n<li>以前留下来的bug,想修复下,但忘记告知测试</li>\n<li>在开发这个需求时,其他产品又找过来让加功能,并说功能很小,没有发邮件通知相关测试人员</li>\n<li>看到某块以前写的代码太乱了,就想着优化下,自信心很高,不必告诉测试</li>\n</ol>\n<p>那这时候你提交的代码,如果不在测试范围又出了问题,只能研发自己抗。并且在所有的研发团队,几乎是不会让夹带需求上线的,这样的做完了不算功劳,做出了问题还会被骂。</p>\n<p>所以,千万不要私自夹带!哪怕你是好心!</p>\n<h3 id=\"7-异常流程\"><a href=\"#7-异常流程\" class=\"headerlink\" title=\"7. 异常流程\"></a>7. 异常流程</h3><p><code>擦屁屁的纸,80%的面积都是保护手的!</code></p>\n<p>这句话是我经常用的,因为我们编程很多时候都是在处理异常流程,正常流程往往并不难,难的是分析出这段开发的代码有多少异常流程有没有处理。</p>\n<p>那么,会有哪些异常呢?</p>\n<ol>\n<li>支付成功MQ消息发送失败,需要worker补偿</li>\n<li>PRC接口调用失败,网络超时,实际成功</li>\n<li>接口幂等性,多次调用结果一致性</li>\n</ol>\n<p>等等,这些都是异常流程,尤其在一些交易提现环节,会出现各种异常,那么不可能把这些异常都反馈用户展示到界面。而是要有一些非常友好的提示,并且在服务端的流程里,有一定的补偿机制,来保证最终的调用成功,或者逆反。</p>\n<h3 id=\"8-代码成坨\"><a href=\"#8-代码成坨\" class=\"headerlink\" title=\"8. 代码成坨\"></a>8. 代码成坨</h3><p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-04.png\" alt=\"小傅哥 & 代码成坨\"></p>\n<p><code>CRUD往往可能是因为你的设计,换个人写也许不同</code></p>\n<p>很多时候研发写代码,根本不考虑是否要扩展,总之一个类 + 几十行ifelse,能搞定所有需求。等下次在开发类似的,就粘贴过去再修修补补,能用就行。</p>\n<p>缺少写出良好代码的研发,一方面是经历有限,另外一方面是学了很多理论但是不好落地。比如设计模式,但自己实际写代码的时候还是很晕。</p>\n<p>这里推荐一本我写的《重学Java设计模式》,全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、包含交易、营销、秒杀、中间件、源码等22个真实场景。可以添加<a href=\"https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html\">小傅哥微信获取:fustack</a></p>\n<h3 id=\"9-SQL性能\"><a href=\"#9-SQL性能\" class=\"headerlink\" title=\"9. SQL性能\"></a>9. SQL性能</h3><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">select</span> <span class=\"operator\">*</span> <span class=\"keyword\">from</span> <span class=\"keyword\">table</span> <span class=\"keyword\">where</span> status <span class=\"operator\">=</span> <span class=\"number\">1</span> limit <span class=\"number\">200</span>;</span><br></pre></td></tr></table></figure>\n<p><strong>这是一段定时任务扫描库表的SQL</strong>,这段sql会定时扫库,将库表中状态是1的扫描出来进行处理,每次扫描200行。你发现有什么问题了吗?</p>\n<ol>\n<li>扫描必要字段即可,不需要全部字段</li>\n<li>这段sql会越来越慢,即使状态字段加了索引。因为<code>status</code>并不能大量排掉其他状态字段,随着数据越来越多依然是全表扫描。</li>\n</ol>\n<p>那么怎么优化呢,其实优化也比较简单,需要先根据状态查询到符合条件的最小的id,之后再sql的查询条件中添加<code>id > xx</code>,即可。另外如果你的任务需要多个worker扫描,增加效率,可以增加门牌号设计,提升扫描效率,如下;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-05.png\" alt=\"小傅哥 & 门牌号扫描\"></p>\n<h3 id=\"10-结伴编程\"><a href=\"#10-结伴编程\" class=\"headerlink\" title=\"10. 结伴编程\"></a>10. 结伴编程</h3><p>评审代码最后这点想说说,<code>陪伴式开发</code>,可能这不是结伴编程,不是共同合作,而是一个研发需要另外一个研发不断的提供帮助。有时候可能就是很简单的问题,也不想查,或者说没有意识去查,只是问。</p>\n<p>业务开发的过程,只要把流程定下来,研发设计评审完,其他的开发过程中遇到的小点并不难,只要查一查就可以搞定。当日也不是说完全不能问,只不过特别普遍,简单的代码问题,自己搞定就可以了,但这个时候还像保姆似的陪伴,就会拖累整个团队的进展,最终大家都需要扛起那个慢的。</p>\n<p>所以,如果你是那个需要陪伴的,要及早断奶,学会自己攻克,快速成长。而如果你是那个卷纸,可哪擦屁股的,要把卷纸传递给他。一个人擦一次是能力体现,反反复复擦一个人,就惹屎上身了。</p>\n<h2 id=\"四、总结\"><a href=\"#四、总结\" class=\"headerlink\" title=\"四、总结\"></a>四、总结</h2><ul>\n<li>以上介绍了代码评审中涉及到的比较常见的点,基本也是很多研发容易忽略和犯错误的地方。这些问题点但拿出哪一个看,都不大。但运行在代码中,确都有可能发生致命或者麻烦的事情。</li>\n<li>想让自己能把代码写好,就不只面试时候造飞机的回答,什么时间复杂度、什么可重入锁、什么红黑树,什么DDD,只要你不能正确的落地和运用这些技术,说的再多都是空谈。</li>\n<li>多学一些、多看一些、多问一些,没有坏处,但要自己能成长,把吸取到的经验心得,运用到业务开发中,写出可扩展、可维护的代码,才能让自己真的升职加薪。也能让既有留下的本事,也有出去的能力。</li>\n</ul>\n<h2 id=\"五、系列推荐\"><a href=\"#五、系列推荐\" class=\"headerlink\" title=\"五、系列推荐\"></a>五、系列推荐</h2><ul>\n<li><a href=\"https://bugstack.cn/itstack-demo-any/2020/09/06/%E6%8F%A1%E8%8D%89-%E4%BD%A0%E7%AB%9F%E7%84%B6%E5%9C%A8%E4%BB%A3%E7%A0%81%E9%87%8C%E4%B8%8B%E6%AF%92.html\">握草,你竟然在代码里下毒!</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html\">DDD领域驱动设计落地方案</a></li>\n<li><a href=\"https://bugstack.cn/itstack/itstack-demo-design.html\">重学Java设计模式(22个真实开发场景)</a></li>\n<li><a href=\"https://bugstack.cn/itstack/interview.html\">面经手册(上最快的车,拿最贵的offer)</a></li>\n<li><a href=\"https://bugstack.cn/itstack/itstack-demo-bytecode.html\">字节码编程(非入侵式全链路监控实践)</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><code>好的代码往往也很好看</code></p>\n<p>代码是给机器运行的,但同样也是给人看的,并且随着上线还需要由人来运维。那么写出<code>可扩展</code>、<code>易维护</code>、<code>好读懂</code>的代码就显得非常重要。</p>\n<p>对于新人来说,互联网大厂项目开发与平常自己学习的代码还是有很大的差别的。日常学习时候通常只要能运行出结果即可,并不会有其他的要求。也不会说有;PRD评审、研发设计评审、代码开发、代码评审以及中间一些列的提交物,直到测试完成,上线验证,开量对外等等。</p>\n<p>所以很多新人刚从学校毕业或者从小公司进入大厂,在规范制约下会有一些不习惯,甚至犯错误。那么为了让大家更好的知晓这些问题,小傅哥特意整理了一些例子,欢迎参考。</p>","more":"<h2 id=\"二、会议室\"><a href=\"#二、会议室\" class=\"headerlink\" title=\"二、会议室\"></a>二、会议室</h2><p><code>谢飞机</code>,刚刚入职没多久,兴奋的写着leader给的需求,🐎码的飞快。恰巧组长走过来:“飞机,带着你的电脑,跟我来码云会议室,做下代码评审。”</p>\n<p><strong>leader</strong>:飞机,你这代码咋这么粗鲁!</p>\n<p><strong>飞机</strong>:啊?😱</p>\n<p><strong>leader</strong>:我要不拦着你,我感觉你这代码都能飞。</p>\n<p><strong>leader</strong>:你看哈,就说这行,这日志打的,上线后出了问题,你能查到原因吗?</p>\n<p><strong>飞机</strong>:好像…</p>\n<p><strong>leader</strong>:还有这,这idea都提示你了,都报黄色了,你怎么不看看。还有,这代码也不格式化,一个月后它认识你,你还认识它吗。</p>\n<p><strong>leader</strong>:给你发的入职编码规范看了?</p>\n<p><strong>飞机</strong>:哦,看一些,写的时候忘了。</p>\n<p><strong>leader</strong>:先别着急写,看会了再写代码,这还有一个不错的工程:<a href=\"https://bugstack.cn/itstack-demo-netty-3/2020/03/04/Netty+JavaFx%E5%AE%9E%E6%88%98-%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9.html\">《Netty+JavaFx实战:仿桌面版微信聊天》</a>,可以参考。</p>\n<p><code>写代码不是以完成功能就算完事,还需要写的漂亮。评审后,飞机,坐回工位,收起了躁动的心,安心熟读手册并练习。</code></p>\n<h2 id=\"三、代码评审\"><a href=\"#三、代码评审\" class=\"headerlink\" title=\"三、代码评审\"></a>三、代码评审</h2><h3 id=\"1-日志规范\"><a href=\"#1-日志规范\" class=\"headerlink\" title=\"1. 日志规范\"></a>1. 日志规范</h3><p>日志是整个代码开发过程中非常重要的环节,如果日志打的不好,那么遇到的线上bug就没法快速定位,定位不了问题也就没法快速解决问题。直接带来的结果可能包括;客诉更多、资损更大、修复更慢。</p>\n<p><strong>就像下面这段代码中的日志</strong>;</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> Result <span class=\"title\">execRule</span><span class=\"params\">(RuleReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则 req:{}"</span>, JSON.toJSONString(req));</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildSuccess();</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(<span class=\"string\">"执行服务规则失败"</span>, e);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildError(e);</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li>看似没什么问题,但在这段异常代码中,没有打方法的入参信息。如果方法异常时只是抛出一些异常栈信息,那么是很难定位具体的由次调用触发的。</li>\n<li>另外如果你的系统监控服务,没有类似方法跟踪ID的功能,最好还需要在日志中把本次调用具有标识性的id,作为查询条件打到日志中。</li>\n</ul>\n<p><strong>修改后的日志</strong>:</p>\n<figure class=\"highlight java\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> Result <span class=\"title\">execRule</span><span class=\"params\">(RuleReq req)</span> </span>{</span><br><span class=\"line\"> <span class=\"keyword\">try</span> {</span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则{}开始 req:{}"</span>, req.getrId(), JSON.toJSONString(req));</span><br><span class=\"line\"> <span class=\"comment\">// 业务流程</span></span><br><span class=\"line\"> logger.info(<span class=\"string\">"执行服务规则{}完成 res:{}"</span>, req.getrId(), <span class=\"string\">"业务流程,必要的结果信息"</span>);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildSuccess();</span><br><span class=\"line\"> } <span class=\"keyword\">catch</span> (Exception e) {</span><br><span class=\"line\"> logger.error(<span class=\"string\">"执行服务规则{}失败 req:{}"</span>, req.getrId(), JSON.toJSONString(req), e);</span><br><span class=\"line\"> <span class=\"keyword\">return</span> Result.buildError(e);</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<ul>\n<li>那么现在这样改成这样打日志,就可以非常方便的查询问题,例如搜索;<code>执行服务规则100098921</code>,那么它的一整串关于这次调用的信息就可以都搜索出来了,方便排查问题。</li>\n<li>在异常中打印入参是为了更加方便的定位问题,不需要比对上下文。</li>\n<li>打日志还有很多技巧,但所有打的日志目的都为了在出问题时可以快速定位问题,但也注意不要打太多日志,精简好用即可。</li>\n</ul>\n<h3 id=\"2-IDEA提示\"><a href=\"#2-IDEA提示\" class=\"headerlink\" title=\"2. IDEA提示\"></a>2. IDEA提示</h3><p>很多时候因为你,走神、疏忽、手滑,写出来的错误代码,<code>IntelliJ IDEA</code>,都会给你警告⚠提示,只是你,没有去看、没有去看、没有去看!</p>\n<p><strong>来自idea的警告</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-01.png\" alt=\"小傅哥 & idea警告\"></p>\n<ul>\n<li>Idea在警告提示这方面非常优秀,只要你能看得见,按照它的提示修改,就可以减少很多的错误。</li>\n<li>如果你还希望有更强的提示,那么你可以按照 <a href=\"https://github.com/alibaba/p3c\"><code>p3c</code></a> 插件,帮你检查代码错误。</li>\n</ul>\n<h3 id=\"3-代码格式\"><a href=\"#3-代码格式\" class=\"headerlink\" title=\"3. 代码格式\"></a>3. 代码格式</h3><p>可能这并不是一个致命的问题,但代码格式化最大的好处是,提升可读性、规整性、以及可以让整组人都在一个标准下执行。因为很多时候一个组的程序员,会在一个类下开发,有人格式化、有人不格式化除了不好看以外,合并代码有时候也会遇到麻烦。</p>\n<p><strong>不格式化的代码缺少灵魂</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-02.png\" alt=\"小傅哥 & 代码格式化\"></p>\n<ul>\n<li>对于严格要自己的程序员来说,代码没有格式化还是很难受的。</li>\n<li>看一段代码,只要发现差一个空格位置,都知道这是格式化还是没格式化。</li>\n</ul>\n<h3 id=\"4-单元测试\"><a href=\"#4-单元测试\" class=\"headerlink\" title=\"4. 单元测试\"></a>4. 单元测试</h3><p>单测?覆盖率?写代码不是写完就可以了吗?</p>\n<p>当然不是,你写的代码你需要保证它能你跑通你所有的流程节点,确保这份功能是没有问题的,才能提交给测试,否则来回反复,耗时耗力。这也就是写单测的目的!甚至好一点的研发可以通过单测驱动开发,在这个阶段能把一些共用的方法合并、抽离,避免过多的冗余方法。</p>\n<p><strong>单测长什么样</strong>;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-03.png\"></p>\n<ul>\n<li>单测完整基本也就是代码的健壮性更好,能把单测写好,基本提交的代码就不会有那么多测试妹子找你聊天。</li>\n<li>在很多公司中一般都会要求单测覆盖率超过多少,否则是不允许编译提交的,这有插件可以和<code>Jenkins</code>配合使用。</li>\n</ul>\n<h3 id=\"5-分支规范\"><a href=\"#5-分支规范\" class=\"headerlink\" title=\"5. 分支规范\"></a>5. 分支规范</h3><p>可能有些人看到<code>分支规范</code>根本没有感觉,因为他们开发的项目较小,没有多人开发,上线周期也短,也不会开发中添加需求。</p>\n<p>但在互联网中并不是这样,往往一个系统需要几个人维护,并同时进行开发。一般这里会包括;master分支、test分支、本次需求的分支,有这么多分支怎么用呢,如下;</p>\n<ol>\n<li>master分支,是主分支,也是上线分支,不允许在上面直接修改代码。</li>\n<li>test分支,是测试环境分支,每个人都需要把自己开发完的分支,提测后合并到test分支,交由测试验证。</li>\n<li>需求分支,也是个人开发的分支,同一个需求下,大家在这个分支写代码,当然也可能这个系统模块的分支就一个人在开发。</li>\n</ol>\n<p><strong>重点</strong>,如果有人不遵守分支规范或者压根没概念,把自己的需求代码写在test分支上,并且是多次修改提交都在test分支写。那么就危险了,严重会耽误上线;为什么?</p>\n<ol>\n<li>test分支,是由大家把自己的代码合并过来共用的,那么这个分支就会包含2个或者更多的并行需求,当你需要上线的时候,需要把自己的代码合并到master,但test分支代码是不能合并到master的,那么多未知的内容,根本没有在上线范围。</li>\n<li>那么你又想上线,又不能避开test分支,就需要把你写的代码,重新粘贴过去,这个时间成本非常大。</li>\n<li>test分支,还随时有删除重新拉的可能,如果有人通知大家删除重新拉,那你的代码就会丢失。</li>\n</ol>\n<h3 id=\"6-夹带需求\"><a href=\"#6-夹带需求\" class=\"headerlink\" title=\"6. 夹带需求\"></a>6. 夹带需求</h3><p><code>提交测试,但还藏一个需求</code></p>\n<p>研发开发需求代码时候,有时候会额外加一些其他代码,而且这些代码可能跟本次需求并没有关系。那为什么会这样呢?</p>\n<ol>\n<li>以前留下来的bug,想修复下,但忘记告知测试</li>\n<li>在开发这个需求时,其他产品又找过来让加功能,并说功能很小,没有发邮件通知相关测试人员</li>\n<li>看到某块以前写的代码太乱了,就想着优化下,自信心很高,不必告诉测试</li>\n</ol>\n<p>那这时候你提交的代码,如果不在测试范围又出了问题,只能研发自己抗。并且在所有的研发团队,几乎是不会让夹带需求上线的,这样的做完了不算功劳,做出了问题还会被骂。</p>\n<p>所以,千万不要私自夹带!哪怕你是好心!</p>\n<h3 id=\"7-异常流程\"><a href=\"#7-异常流程\" class=\"headerlink\" title=\"7. 异常流程\"></a>7. 异常流程</h3><p><code>擦屁屁的纸,80%的面积都是保护手的!</code></p>\n<p>这句话是我经常用的,因为我们编程很多时候都是在处理异常流程,正常流程往往并不难,难的是分析出这段开发的代码有多少异常流程有没有处理。</p>\n<p>那么,会有哪些异常呢?</p>\n<ol>\n<li>支付成功MQ消息发送失败,需要worker补偿</li>\n<li>PRC接口调用失败,网络超时,实际成功</li>\n<li>接口幂等性,多次调用结果一致性</li>\n</ol>\n<p>等等,这些都是异常流程,尤其在一些交易提现环节,会出现各种异常,那么不可能把这些异常都反馈用户展示到界面。而是要有一些非常友好的提示,并且在服务端的流程里,有一定的补偿机制,来保证最终的调用成功,或者逆反。</p>\n<h3 id=\"8-代码成坨\"><a href=\"#8-代码成坨\" class=\"headerlink\" title=\"8. 代码成坨\"></a>8. 代码成坨</h3><p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-04.png\" alt=\"小傅哥 & 代码成坨\"></p>\n<p><code>CRUD往往可能是因为你的设计,换个人写也许不同</code></p>\n<p>很多时候研发写代码,根本不考虑是否要扩展,总之一个类 + 几十行ifelse,能搞定所有需求。等下次在开发类似的,就粘贴过去再修修补补,能用就行。</p>\n<p>缺少写出良好代码的研发,一方面是经历有限,另外一方面是学了很多理论但是不好落地。比如设计模式,但自己实际写代码的时候还是很晕。</p>\n<p>这里推荐一本我写的《重学Java设计模式》,全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、包含交易、营销、秒杀、中间件、源码等22个真实场景。可以添加<a href=\"https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html\">小傅哥微信获取:fustack</a></p>\n<h3 id=\"9-SQL性能\"><a href=\"#9-SQL性能\" class=\"headerlink\" title=\"9. SQL性能\"></a>9. SQL性能</h3><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">select</span> <span class=\"operator\">*</span> <span class=\"keyword\">from</span> <span class=\"keyword\">table</span> <span class=\"keyword\">where</span> status <span class=\"operator\">=</span> <span class=\"number\">1</span> limit <span class=\"number\">200</span>;</span><br></pre></td></tr></table></figure>\n<p><strong>这是一段定时任务扫描库表的SQL</strong>,这段sql会定时扫库,将库表中状态是1的扫描出来进行处理,每次扫描200行。你发现有什么问题了吗?</p>\n<ol>\n<li>扫描必要字段即可,不需要全部字段</li>\n<li>这段sql会越来越慢,即使状态字段加了索引。因为<code>status</code>并不能大量排掉其他状态字段,随着数据越来越多依然是全表扫描。</li>\n</ol>\n<p>那么怎么优化呢,其实优化也比较简单,需要先根据状态查询到符合条件的最小的id,之后再sql的查询条件中添加<code>id > xx</code>,即可。另外如果你的任务需要多个worker扫描,增加效率,可以增加门牌号设计,提升扫描效率,如下;</p>\n<p><img src=\"https://bugstack.cn/assets/images/2020/interview/interview-all-05.png\" alt=\"小傅哥 & 门牌号扫描\"></p>\n<h3 id=\"10-结伴编程\"><a href=\"#10-结伴编程\" class=\"headerlink\" title=\"10. 结伴编程\"></a>10. 结伴编程</h3><p>评审代码最后这点想说说,<code>陪伴式开发</code>,可能这不是结伴编程,不是共同合作,而是一个研发需要另外一个研发不断的提供帮助。有时候可能就是很简单的问题,也不想查,或者说没有意识去查,只是问。</p>\n<p>业务开发的过程,只要把流程定下来,研发设计评审完,其他的开发过程中遇到的小点并不难,只要查一查就可以搞定。当日也不是说完全不能问,只不过特别普遍,简单的代码问题,自己搞定就可以了,但这个时候还像保姆似的陪伴,就会拖累整个团队的进展,最终大家都需要扛起那个慢的。</p>\n<p>所以,如果你是那个需要陪伴的,要及早断奶,学会自己攻克,快速成长。而如果你是那个卷纸,可哪擦屁股的,要把卷纸传递给他。一个人擦一次是能力体现,反反复复擦一个人,就惹屎上身了。</p>\n<h2 id=\"四、总结\"><a href=\"#四、总结\" class=\"headerlink\" title=\"四、总结\"></a>四、总结</h2><ul>\n<li>以上介绍了代码评审中涉及到的比较常见的点,基本也是很多研发容易忽略和犯错误的地方。这些问题点但拿出哪一个看,都不大。但运行在代码中,确都有可能发生致命或者麻烦的事情。</li>\n<li>想让自己能把代码写好,就不只面试时候造飞机的回答,什么时间复杂度、什么可重入锁、什么红黑树,什么DDD,只要你不能正确的落地和运用这些技术,说的再多都是空谈。</li>\n<li>多学一些、多看一些、多问一些,没有坏处,但要自己能成长,把吸取到的经验心得,运用到业务开发中,写出可扩展、可维护的代码,才能让自己真的升职加薪。也能让既有留下的本事,也有出去的能力。</li>\n</ul>\n<h2 id=\"五、系列推荐\"><a href=\"#五、系列推荐\" class=\"headerlink\" title=\"五、系列推荐\"></a>五、系列推荐</h2><ul>\n<li><a href=\"https://bugstack.cn/itstack-demo-any/2020/09/06/%E6%8F%A1%E8%8D%89-%E4%BD%A0%E7%AB%9F%E7%84%B6%E5%9C%A8%E4%BB%A3%E7%A0%81%E9%87%8C%E4%B8%8B%E6%AF%92.html\">握草,你竟然在代码里下毒!</a></li>\n<li><a href=\"https://bugstack.cn/itstack-demo-ddd/itstack-demo-ddd.html\">DDD领域驱动设计落地方案</a></li>\n<li><a href=\"https://bugstack.cn/itstack/itstack-demo-design.html\">重学Java设计模式(22个真实开发场景)</a></li>\n<li><a href=\"https://bugstack.cn/itstack/interview.html\">面经手册(上最快的车,拿最贵的offer)</a></li>\n<li><a href=\"https://bugstack.cn/itstack/itstack-demo-bytecode.html\">字节码编程(非入侵式全链路监控实践)</a></li>\n</ul>"},{"layout":"post","title":"《重学Java设计模式》PDF 下载","date":"2021-01-23T06:12:00.000Z","comments":1,"_content":"\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n>沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n**我膨胀了💥**,在编写完上一本PDF《字节码编程》被下载了2000份以后,蠢蠢欲动开始计划第二本。于是从🌹5月20日那天投身实战型设计模式打磨,通过模拟互联网业务开发实际需求作为学习场景,讲解设计模式。\n\n**全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。**\n\n\n\n💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。\n\n<!-- more -->\n\n## 二、简介\n\n\n\n欢迎来到这里,很高兴你`将`拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。\n\n可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积`ifelse`组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑`Ctrl+C`、`Ctrl+V`。\n\n所以为了能让更多的程序员👨💻更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者`小傅哥`,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。\n\n### 1. 谁发明了设计模式?\n\n设计模式的概念最早是由 `克里斯托佛·亚历山大` 在其著作 `《建筑模式语言》` 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,`埃里希·伽玛`、 `约翰·弗利赛德斯`、 `拉尔夫·约翰逊` 和 `理查德·赫尔姆` 这四位作者接受了模式的概念。 1994 年, 他们出版了 `《设计模式: 可复用面向对象软件的基础》` 一书, 将设计模式的概念应用到程序开发领域中。 \n\n其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。\n\n### 2. 我怎么学不会设计模式?\n\n钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌!\n\n**所以**,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是*人车合一*,才能站在设计模式的基础上构建出更加合理的代码。\n\n### 3. 适合人群\n\n1. 具备一定编程基础在工作1-3年的研发人员\n2. 希望通过此书提升编码思维,剔除到代码中的坏味道\n3. 有意愿成为架构师,但还处在一定瓶颈期\n4. 学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍\n\n### 4. 我能学到什么\n\n1. 优化平时开发中的ifelse语句,让代码更加整洁\n2. 看设计模式不再是用理论生搬硬套,这次可以有点用\n3. 站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用\n4. 升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙\n\n### 5. 阅读建议\n\n本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。\n\n## 三、书中目录\n\n设计模式遵循六大原则;单一职责(`一个类和方法只做一件事`)、里氏替换(`多态,子类可扩展父类`)、依赖倒置(`细节依赖抽象,下层依赖上层`)、接口隔离(`建立单一接口`)、迪米特原则(`最少知道,降低耦合`)、开闭原则(`抽象架构,扩展实现`),会在具体的设计模式章节中,进行体现。\n\n### 1. 创建型模式\n\n**这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ------------ | --------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------ |\n| 1 | **工厂方法** |  | 多种类型商品不同接口,统一发奖服务搭建场景 | 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 |\n| 2 | **抽象工厂** |  | 替换Redis双集群升级,代理类抽象场景 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 |\n| 3 | **生成器** |  | 各项装修物料组合套餐选配场景 | 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 |\n| 4 | **原型** |  | 上机考试多套试,每人题目和答案乱序排列场景 | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 |\n| 5 | **单例** |  | 7种单例模式案例,Effective Java 作者推荐枚举单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 |\n\n\n\n### 2. 结构型模式\n\n**这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n| 1 | **适配器** |  | 从多个MQ消息体中,抽取指定字段值场景 | 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |\n| 2 | **桥接** |  | 多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景 | 将抽象部分与实现部分分离,使它们都可以独立的变化。 |\n| 3 | **组合** |  | 营销差异化人群发券,决策树引擎搭建场景 | 将对象组合成树形结构以表示\"部分-整体\"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 |\n| 4 | **装饰** |  | SSO单点登录功能扩展,增加拦截用户访问方法范围场景 | 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。 |\n| 5 | **外观** |  | 基于SpringBoot开发门面模式中间件,统一控制接口白名单场景 | 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |\n| 6 | **享元** |  | 基于Redis秒杀,提供活动与库存信息查询场景 | 运用共享技术有效地支持大量细粒度的对象。 |\n| 7 | **代理** |  | 模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景 | 为其他对象提供一种代理以控制对这个对象的访问。 |\n\n### 3. 行为模式\n\n**这类模式负责对象间的高效沟通和职责委派。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ------------ | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n| 1 | **责任链** |  | 模拟618电商大促期间,项目上线流程多级负责人审批场景 | 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 |\n| 2 | **命令** |  | 模拟高档餐厅八大菜系,小二点单厨师烹饪场景 | 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 |\n| 3 | **迭代器** |  | 模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景 | 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 |\n| 4 | **中介者** |  | 按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景 | 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 |\n| 5 | **备忘录** |  | 模拟互联网系统上线过程中,配置文件回滚场景 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 |\n| 6 | **观察者** |  | 模拟类似小客车指标摇号过程,监听消息通知用户中签场景 | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 |\n| 7 | **状态** |  | 模拟系统营销活动,状态流程审核发布上线场景 | 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 |\n| 8 | **策略** |  | 模拟多种营销类型优惠券,折扣金额计算策略场景 | 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 |\n| 9 | **模板方法** |  | 模拟爬虫各类电商商品,生成营销推广海报场景 | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 |\n| 10 | **访问者** |  | 模拟家长与校长,对学生和老师的不同视角信息的访问场景 | 主要将数据结构与数据操作分离。 |\n\n*以上图稿和部分描述参考;https://refactoringguru.cn、https://www.runoob.com/design-pattern/visitor-pattern.html*\n\n\n## 四、PDF📚下载\n\n下载前,一点对原创作者的支持请求😬,`点赞`、`在看`、`分享`、`留言`、`赞赏`,完成任何一样都可以获得🉐这本PDF书籍。\n\n### 1. 可获得内容包括\n\n1. `《重学 Java 设计模式》`PDF 书籍一本\n2. 59个对应的工程案例源码一套\n3. 在线阅读版学习了资料\n\n### 2. 获取方式\n\n1. 扫描下方二维码加专栏学习群,凡进群者都送书籍一本\n2. 添加小傅哥微信(fustack)获取PDF书籍\n3. 公众号内回复PDF下载,你会获得一个连接,打开后右侧**菜单** -> `精选` -> `值得一看的好书`,里面对应也有这本书籍📚\n\n\n\n## 五、收个尾🎉\n\n👣走过的路会留下足迹,👨💻码过的文会盛满四季。\n\n有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。**也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈**\n\n本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。\n\n**最后,我想说**:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有`能留下的本事`和`跳出去的能力`,才会相对安稳度过动荡。","source":"_posts/重学 Java 设计模式.md","raw":"---\nlayout: post\ntitle: \"《重学Java设计模式》PDF 下载\"\ndate: 2021-01-23 14:12\ncomments: true\ntags: \n\t- java \n\t- 设计模式 \n---\n\n作者:小傅哥\n博客:[https://bugstack.cn](https://bugstack.cn)\n\n>沉淀、分享、成长,让自己和他人都能有所收获!😄\n\n## 一、前言\n\n**我膨胀了💥**,在编写完上一本PDF《字节码编程》被下载了2000份以后,蠢蠢欲动开始计划第二本。于是从🌹5月20日那天投身实战型设计模式打磨,通过模拟互联网业务开发实际需求作为学习场景,讲解设计模式。\n\n**全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。**\n\n\n\n💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。\n\n<!-- more -->\n\n## 二、简介\n\n\n\n欢迎来到这里,很高兴你`将`拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。\n\n可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积`ifelse`组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑`Ctrl+C`、`Ctrl+V`。\n\n所以为了能让更多的程序员👨💻更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者`小傅哥`,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。\n\n### 1. 谁发明了设计模式?\n\n设计模式的概念最早是由 `克里斯托佛·亚历山大` 在其著作 `《建筑模式语言》` 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,`埃里希·伽玛`、 `约翰·弗利赛德斯`、 `拉尔夫·约翰逊` 和 `理查德·赫尔姆` 这四位作者接受了模式的概念。 1994 年, 他们出版了 `《设计模式: 可复用面向对象软件的基础》` 一书, 将设计模式的概念应用到程序开发领域中。 \n\n其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。\n\n### 2. 我怎么学不会设计模式?\n\n钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌!\n\n**所以**,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是*人车合一*,才能站在设计模式的基础上构建出更加合理的代码。\n\n### 3. 适合人群\n\n1. 具备一定编程基础在工作1-3年的研发人员\n2. 希望通过此书提升编码思维,剔除到代码中的坏味道\n3. 有意愿成为架构师,但还处在一定瓶颈期\n4. 学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍\n\n### 4. 我能学到什么\n\n1. 优化平时开发中的ifelse语句,让代码更加整洁\n2. 看设计模式不再是用理论生搬硬套,这次可以有点用\n3. 站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用\n4. 升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙\n\n### 5. 阅读建议\n\n本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。\n\n## 三、书中目录\n\n设计模式遵循六大原则;单一职责(`一个类和方法只做一件事`)、里氏替换(`多态,子类可扩展父类`)、依赖倒置(`细节依赖抽象,下层依赖上层`)、接口隔离(`建立单一接口`)、迪米特原则(`最少知道,降低耦合`)、开闭原则(`抽象架构,扩展实现`),会在具体的设计模式章节中,进行体现。\n\n### 1. 创建型模式\n\n**这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ------------ | --------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------ |\n| 1 | **工厂方法** |  | 多种类型商品不同接口,统一发奖服务搭建场景 | 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 |\n| 2 | **抽象工厂** |  | 替换Redis双集群升级,代理类抽象场景 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 |\n| 3 | **生成器** |  | 各项装修物料组合套餐选配场景 | 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 |\n| 4 | **原型** |  | 上机考试多套试,每人题目和答案乱序排列场景 | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 |\n| 5 | **单例** |  | 7种单例模式案例,Effective Java 作者推荐枚举单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 |\n\n\n\n### 2. 结构型模式\n\n**这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n| 1 | **适配器** |  | 从多个MQ消息体中,抽取指定字段值场景 | 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |\n| 2 | **桥接** |  | 多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景 | 将抽象部分与实现部分分离,使它们都可以独立的变化。 |\n| 3 | **组合** |  | 营销差异化人群发券,决策树引擎搭建场景 | 将对象组合成树形结构以表示\"部分-整体\"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 |\n| 4 | **装饰** |  | SSO单点登录功能扩展,增加拦截用户访问方法范围场景 | 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。 |\n| 5 | **外观** |  | 基于SpringBoot开发门面模式中间件,统一控制接口白名单场景 | 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |\n| 6 | **享元** |  | 基于Redis秒杀,提供活动与库存信息查询场景 | 运用共享技术有效地支持大量细粒度的对象。 |\n| 7 | **代理** |  | 模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景 | 为其他对象提供一种代理以控制对这个对象的访问。 |\n\n### 3. 行为模式\n\n**这类模式负责对象间的高效沟通和职责委派。**\n\n| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 |\n| ---- | ------------ | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n| 1 | **责任链** |  | 模拟618电商大促期间,项目上线流程多级负责人审批场景 | 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 |\n| 2 | **命令** |  | 模拟高档餐厅八大菜系,小二点单厨师烹饪场景 | 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 |\n| 3 | **迭代器** |  | 模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景 | 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 |\n| 4 | **中介者** |  | 按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景 | 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 |\n| 5 | **备忘录** |  | 模拟互联网系统上线过程中,配置文件回滚场景 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 |\n| 6 | **观察者** |  | 模拟类似小客车指标摇号过程,监听消息通知用户中签场景 | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 |\n| 7 | **状态** |  | 模拟系统营销活动,状态流程审核发布上线场景 | 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 |\n| 8 | **策略** |  | 模拟多种营销类型优惠券,折扣金额计算策略场景 | 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 |\n| 9 | **模板方法** |  | 模拟爬虫各类电商商品,生成营销推广海报场景 | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 |\n| 10 | **访问者** |  | 模拟家长与校长,对学生和老师的不同视角信息的访问场景 | 主要将数据结构与数据操作分离。 |\n\n*以上图稿和部分描述参考;https://refactoringguru.cn、https://www.runoob.com/design-pattern/visitor-pattern.html*\n\n\n## 四、PDF📚下载\n\n下载前,一点对原创作者的支持请求😬,`点赞`、`在看`、`分享`、`留言`、`赞赏`,完成任何一样都可以获得🉐这本PDF书籍。\n\n### 1. 可获得内容包括\n\n1. `《重学 Java 设计模式》`PDF 书籍一本\n2. 59个对应的工程案例源码一套\n3. 在线阅读版学习了资料\n\n### 2. 获取方式\n\n1. 扫描下方二维码加专栏学习群,凡进群者都送书籍一本\n2. 添加小傅哥微信(fustack)获取PDF书籍\n3. 公众号内回复PDF下载,你会获得一个连接,打开后右侧**菜单** -> `精选` -> `值得一看的好书`,里面对应也有这本书籍📚\n\n\n\n## 五、收个尾🎉\n\n👣走过的路会留下足迹,👨💻码过的文会盛满四季。\n\n有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。**也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈**\n\n本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。\n\n**最后,我想说**:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有`能留下的本事`和`跳出去的能力`,才会相对安稳度过动荡。","slug":"重学 Java 设计模式","published":1,"updated":"2021-01-23T08:40:30.227Z","_id":"ckk9gom470002t99l22s4hxa7","photos":[],"link":"","content":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><strong>我膨胀了💥</strong>,在编写完上一本PDF《字节码编程》被下载了2000份以后,蠢蠢欲动开始计划第二本。于是从🌹5月20日那天投身实战型设计模式打磨,通过模拟互联网业务开发实际需求作为学习场景,讲解设计模式。</p>\n<p><strong>全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/illustration/swell.png\"></p>\n<p>💋<code>鉴于作者水平有限</code>,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。</p>\n<a id=\"more\"></a>\n\n<h2 id=\"二、简介\"><a href=\"#二、简介\" class=\"headerlink\" title=\"二、简介\"></a>二、简介</h2><p><img src=\"https://bugstack.cn/assets/images/2020/design/pdflogo.png\"></p>\n<p>欢迎来到这里,很高兴你<code>将</code>拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。</p>\n<p>可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积<code>ifelse</code>组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑<code>Ctrl+C</code>、<code>Ctrl+V</code>。</p>\n<p>所以为了能让更多的程序员👨💻更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者<code>小傅哥</code>,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。</p>\n<h3 id=\"1-谁发明了设计模式?\"><a href=\"#1-谁发明了设计模式?\" class=\"headerlink\" title=\"1. 谁发明了设计模式?\"></a>1. 谁发明了设计模式?</h3><p>设计模式的概念最早是由 <code>克里斯托佛·亚历山大</code> 在其著作 <code>《建筑模式语言》</code> 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,<code>埃里希·伽玛</code>、 <code>约翰·弗利赛德斯</code>、 <code>拉尔夫·约翰逊</code> 和 <code>理查德·赫尔姆</code> 这四位作者接受了模式的概念。 1994 年, 他们出版了 <code>《设计模式: 可复用面向对象软件的基础》</code> 一书, 将设计模式的概念应用到程序开发领域中。 </p>\n<p>其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。</p>\n<h3 id=\"2-我怎么学不会设计模式?\"><a href=\"#2-我怎么学不会设计模式?\" class=\"headerlink\" title=\"2. 我怎么学不会设计模式?\"></a>2. 我怎么学不会设计模式?</h3><p>钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌!</p>\n<p><strong>所以</strong>,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是<em>人车合一</em>,才能站在设计模式的基础上构建出更加合理的代码。</p>\n<h3 id=\"3-适合人群\"><a href=\"#3-适合人群\" class=\"headerlink\" title=\"3. 适合人群\"></a>3. 适合人群</h3><ol>\n<li>具备一定编程基础在工作1-3年的研发人员</li>\n<li>希望通过此书提升编码思维,剔除到代码中的坏味道</li>\n<li>有意愿成为架构师,但还处在一定瓶颈期</li>\n<li>学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍</li>\n</ol>\n<h3 id=\"4-我能学到什么\"><a href=\"#4-我能学到什么\" class=\"headerlink\" title=\"4. 我能学到什么\"></a>4. 我能学到什么</h3><ol>\n<li>优化平时开发中的ifelse语句,让代码更加整洁</li>\n<li>看设计模式不再是用理论生搬硬套,这次可以有点用</li>\n<li>站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用</li>\n<li>升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙</li>\n</ol>\n<h3 id=\"5-阅读建议\"><a href=\"#5-阅读建议\" class=\"headerlink\" title=\"5. 阅读建议\"></a>5. 阅读建议</h3><p>本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。</p>\n<h2 id=\"三、书中目录\"><a href=\"#三、书中目录\" class=\"headerlink\" title=\"三、书中目录\"></a>三、书中目录</h2><p>设计模式遵循六大原则;单一职责(<code>一个类和方法只做一件事</code>)、里氏替换(<code>多态,子类可扩展父类</code>)、依赖倒置(<code>细节依赖抽象,下层依赖上层</code>)、接口隔离(<code>建立单一接口</code>)、迪米特原则(<code>最少知道,降低耦合</code>)、开闭原则(<code>抽象架构,扩展实现</code>),会在具体的设计模式章节中,进行体现。</p>\n<h3 id=\"1-创建型模式\"><a href=\"#1-创建型模式\" class=\"headerlink\" title=\"1. 创建型模式\"></a>1. 创建型模式</h3><p><strong>这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>工厂方法</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/11.png\"></td>\n<td>多种类型商品不同接口,统一发奖服务搭建场景</td>\n<td>定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>抽象工厂</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/12.png\"></td>\n<td>替换Redis双集群升级,代理类抽象场景</td>\n<td>提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>生成器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/13.png\"></td>\n<td>各项装修物料组合套餐选配场景</td>\n<td>将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>原型</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/14.png\"></td>\n<td>上机考试多套试,每人题目和答案乱序排列场景</td>\n<td>用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>单例</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/15.png\"></td>\n<td>7种单例模式案例,Effective Java 作者推荐枚举单例模式</td>\n<td>保证一个类仅有一个实例,并提供一个访问它的全局访问点。</td>\n</tr>\n</tbody></table>\n<h3 id=\"2-结构型模式\"><a href=\"#2-结构型模式\" class=\"headerlink\" title=\"2. 结构型模式\"></a>2. 结构型模式</h3><p><strong>这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>适配器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/21.png\"></td>\n<td>从多个MQ消息体中,抽取指定字段值场景</td>\n<td>将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>桥接</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/22.png\"></td>\n<td>多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景</td>\n<td>将抽象部分与实现部分分离,使它们都可以独立的变化。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>组合</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/23.png\"></td>\n<td>营销差异化人群发券,决策树引擎搭建场景</td>\n<td>将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>装饰</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/24.png\"></td>\n<td>SSO单点登录功能扩展,增加拦截用户访问方法范围场景</td>\n<td>动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>外观</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/25.png\"></td>\n<td>基于SpringBoot开发门面模式中间件,统一控制接口白名单场景</td>\n<td>为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。</td>\n</tr>\n<tr>\n<td>6</td>\n<td><strong>享元</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/26.png\"></td>\n<td>基于Redis秒杀,提供活动与库存信息查询场景</td>\n<td>运用共享技术有效地支持大量细粒度的对象。</td>\n</tr>\n<tr>\n<td>7</td>\n<td><strong>代理</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/27.png\"></td>\n<td>模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景</td>\n<td>为其他对象提供一种代理以控制对这个对象的访问。</td>\n</tr>\n</tbody></table>\n<h3 id=\"3-行为模式\"><a href=\"#3-行为模式\" class=\"headerlink\" title=\"3. 行为模式\"></a>3. 行为模式</h3><p><strong>这类模式负责对象间的高效沟通和职责委派。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>责任链</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/31.png\"></td>\n<td>模拟618电商大促期间,项目上线流程多级负责人审批场景</td>\n<td>避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>命令</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/32.png\"></td>\n<td>模拟高档餐厅八大菜系,小二点单厨师烹饪场景</td>\n<td>将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>迭代器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/33.png\"></td>\n<td>模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景</td>\n<td>提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>中介者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/34.png\"></td>\n<td>按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景</td>\n<td>用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>备忘录</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/35.png\"></td>\n<td>模拟互联网系统上线过程中,配置文件回滚场景</td>\n<td>在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。</td>\n</tr>\n<tr>\n<td>6</td>\n<td><strong>观察者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/36.png\"></td>\n<td>模拟类似小客车指标摇号过程,监听消息通知用户中签场景</td>\n<td>定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。</td>\n</tr>\n<tr>\n<td>7</td>\n<td><strong>状态</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/37.png\"></td>\n<td>模拟系统营销活动,状态流程审核发布上线场景</td>\n<td>允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。</td>\n</tr>\n<tr>\n<td>8</td>\n<td><strong>策略</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/38.png\"></td>\n<td>模拟多种营销类型优惠券,折扣金额计算策略场景</td>\n<td>定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。</td>\n</tr>\n<tr>\n<td>9</td>\n<td><strong>模板方法</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/39.png\"></td>\n<td>模拟爬虫各类电商商品,生成营销推广海报场景</td>\n<td>定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。</td>\n</tr>\n<tr>\n<td>10</td>\n<td><strong>访问者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/310.png\"></td>\n<td>模拟家长与校长,对学生和老师的不同视角信息的访问场景</td>\n<td>主要将数据结构与数据操作分离。</td>\n</tr>\n</tbody></table>\n<p><em>以上图稿和部分描述参考;<a href=\"https://refactoringguru.cn、https//www.runoob.com/design-pattern/visitor-pattern.html\">https://refactoringguru.cn、https://www.runoob.com/design-pattern/visitor-pattern.html</a></em></p>\n<h2 id=\"四、PDF📚下载\"><a href=\"#四、PDF📚下载\" class=\"headerlink\" title=\"四、PDF📚下载\"></a>四、PDF📚下载</h2><p>下载前,一点对原创作者的支持请求😬,<code>点赞</code>、<code>在看</code>、<code>分享</code>、<code>留言</code>、<code>赞赏</code>,完成任何一样都可以获得🉐这本PDF书籍。</p>\n<h3 id=\"1-可获得内容包括\"><a href=\"#1-可获得内容包括\" class=\"headerlink\" title=\"1. 可获得内容包括\"></a>1. 可获得内容包括</h3><ol>\n<li><code>《重学 Java 设计模式》</code>PDF 书籍一本</li>\n<li>59个对应的工程案例源码一套</li>\n<li>在线阅读版学习了资料</li>\n</ol>\n<h3 id=\"2-获取方式\"><a href=\"#2-获取方式\" class=\"headerlink\" title=\"2. 获取方式\"></a>2. 获取方式</h3><ol>\n<li>扫描下方二维码加专栏学习群,凡进群者都送书籍一本</li>\n<li>添加小傅哥微信(fustack)获取PDF书籍</li>\n<li>公众号内回复PDF下载,你会获得一个连接,打开后右侧<strong>菜单</strong> -> <code>精选</code> -> <code>值得一看的好书</code>,里面对应也有这本书籍📚</li>\n</ol>\n<p><img src=\"https://bugstack.cn/assets/images/2020/design/mkt.png\"></p>\n<h2 id=\"五、收个尾🎉\"><a href=\"#五、收个尾🎉\" class=\"headerlink\" title=\"五、收个尾🎉\"></a>五、收个尾🎉</h2><p>👣走过的路会留下足迹,👨💻码过的文会盛满四季。</p>\n<p>有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。<strong>也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈</strong></p>\n<p>本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。</p>\n<p><strong>最后,我想说</strong>:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有<code>能留下的本事</code>和<code>跳出去的能力</code>,才会相对安稳度过动荡。</p>\n","site":{"data":{}},"excerpt":"<p>作者:小傅哥<br>博客:<a href=\"https://bugstack.cn/\">https://bugstack.cn</a></p>\n<blockquote>\n<p>沉淀、分享、成长,让自己和他人都能有所收获!😄</p>\n</blockquote>\n<h2 id=\"一、前言\"><a href=\"#一、前言\" class=\"headerlink\" title=\"一、前言\"></a>一、前言</h2><p><strong>我膨胀了💥</strong>,在编写完上一本PDF《字节码编程》被下载了2000份以后,蠢蠢欲动开始计划第二本。于是从🌹5月20日那天投身实战型设计模式打磨,通过模拟互联网业务开发实际需求作为学习场景,讲解设计模式。</p>\n<p><strong>全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。</strong></p>\n<p><img src=\"https://bugstack.cn/assets/images/illustration/swell.png\"></p>\n<p>💋<code>鉴于作者水平有限</code>,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。</p>","more":"<h2 id=\"二、简介\"><a href=\"#二、简介\" class=\"headerlink\" title=\"二、简介\"></a>二、简介</h2><p><img src=\"https://bugstack.cn/assets/images/2020/design/pdflogo.png\"></p>\n<p>欢迎来到这里,很高兴你<code>将</code>拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。</p>\n<p>可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积<code>ifelse</code>组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑<code>Ctrl+C</code>、<code>Ctrl+V</code>。</p>\n<p>所以为了能让更多的程序员👨💻更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者<code>小傅哥</code>,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。</p>\n<h3 id=\"1-谁发明了设计模式?\"><a href=\"#1-谁发明了设计模式?\" class=\"headerlink\" title=\"1. 谁发明了设计模式?\"></a>1. 谁发明了设计模式?</h3><p>设计模式的概念最早是由 <code>克里斯托佛·亚历山大</code> 在其著作 <code>《建筑模式语言》</code> 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,<code>埃里希·伽玛</code>、 <code>约翰·弗利赛德斯</code>、 <code>拉尔夫·约翰逊</code> 和 <code>理查德·赫尔姆</code> 这四位作者接受了模式的概念。 1994 年, 他们出版了 <code>《设计模式: 可复用面向对象软件的基础》</code> 一书, 将设计模式的概念应用到程序开发领域中。 </p>\n<p>其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。</p>\n<h3 id=\"2-我怎么学不会设计模式?\"><a href=\"#2-我怎么学不会设计模式?\" class=\"headerlink\" title=\"2. 我怎么学不会设计模式?\"></a>2. 我怎么学不会设计模式?</h3><p>钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌!</p>\n<p><strong>所以</strong>,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是<em>人车合一</em>,才能站在设计模式的基础上构建出更加合理的代码。</p>\n<h3 id=\"3-适合人群\"><a href=\"#3-适合人群\" class=\"headerlink\" title=\"3. 适合人群\"></a>3. 适合人群</h3><ol>\n<li>具备一定编程基础在工作1-3年的研发人员</li>\n<li>希望通过此书提升编码思维,剔除到代码中的坏味道</li>\n<li>有意愿成为架构师,但还处在一定瓶颈期</li>\n<li>学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍</li>\n</ol>\n<h3 id=\"4-我能学到什么\"><a href=\"#4-我能学到什么\" class=\"headerlink\" title=\"4. 我能学到什么\"></a>4. 我能学到什么</h3><ol>\n<li>优化平时开发中的ifelse语句,让代码更加整洁</li>\n<li>看设计模式不再是用理论生搬硬套,这次可以有点用</li>\n<li>站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用</li>\n<li>升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙</li>\n</ol>\n<h3 id=\"5-阅读建议\"><a href=\"#5-阅读建议\" class=\"headerlink\" title=\"5. 阅读建议\"></a>5. 阅读建议</h3><p>本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。</p>\n<h2 id=\"三、书中目录\"><a href=\"#三、书中目录\" class=\"headerlink\" title=\"三、书中目录\"></a>三、书中目录</h2><p>设计模式遵循六大原则;单一职责(<code>一个类和方法只做一件事</code>)、里氏替换(<code>多态,子类可扩展父类</code>)、依赖倒置(<code>细节依赖抽象,下层依赖上层</code>)、接口隔离(<code>建立单一接口</code>)、迪米特原则(<code>最少知道,降低耦合</code>)、开闭原则(<code>抽象架构,扩展实现</code>),会在具体的设计模式章节中,进行体现。</p>\n<h3 id=\"1-创建型模式\"><a href=\"#1-创建型模式\" class=\"headerlink\" title=\"1. 创建型模式\"></a>1. 创建型模式</h3><p><strong>这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>工厂方法</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/11.png\"></td>\n<td>多种类型商品不同接口,统一发奖服务搭建场景</td>\n<td>定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>抽象工厂</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/12.png\"></td>\n<td>替换Redis双集群升级,代理类抽象场景</td>\n<td>提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>生成器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/13.png\"></td>\n<td>各项装修物料组合套餐选配场景</td>\n<td>将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>原型</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/14.png\"></td>\n<td>上机考试多套试,每人题目和答案乱序排列场景</td>\n<td>用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>单例</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/15.png\"></td>\n<td>7种单例模式案例,Effective Java 作者推荐枚举单例模式</td>\n<td>保证一个类仅有一个实例,并提供一个访问它的全局访问点。</td>\n</tr>\n</tbody></table>\n<h3 id=\"2-结构型模式\"><a href=\"#2-结构型模式\" class=\"headerlink\" title=\"2. 结构型模式\"></a>2. 结构型模式</h3><p><strong>这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>适配器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/21.png\"></td>\n<td>从多个MQ消息体中,抽取指定字段值场景</td>\n<td>将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>桥接</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/22.png\"></td>\n<td>多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景</td>\n<td>将抽象部分与实现部分分离,使它们都可以独立的变化。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>组合</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/23.png\"></td>\n<td>营销差异化人群发券,决策树引擎搭建场景</td>\n<td>将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>装饰</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/24.png\"></td>\n<td>SSO单点登录功能扩展,增加拦截用户访问方法范围场景</td>\n<td>动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>外观</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/25.png\"></td>\n<td>基于SpringBoot开发门面模式中间件,统一控制接口白名单场景</td>\n<td>为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。</td>\n</tr>\n<tr>\n<td>6</td>\n<td><strong>享元</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/26.png\"></td>\n<td>基于Redis秒杀,提供活动与库存信息查询场景</td>\n<td>运用共享技术有效地支持大量细粒度的对象。</td>\n</tr>\n<tr>\n<td>7</td>\n<td><strong>代理</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/27.png\"></td>\n<td>模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景</td>\n<td>为其他对象提供一种代理以控制对这个对象的访问。</td>\n</tr>\n</tbody></table>\n<h3 id=\"3-行为模式\"><a href=\"#3-行为模式\" class=\"headerlink\" title=\"3. 行为模式\"></a>3. 行为模式</h3><p><strong>这类模式负责对象间的高效沟通和职责委派。</strong></p>\n<table>\n<thead>\n<tr>\n<th>序号</th>\n<th>类型</th>\n<th>图稿</th>\n<th>业务场景</th>\n<th>实现要点</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>1</td>\n<td><strong>责任链</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/31.png\"></td>\n<td>模拟618电商大促期间,项目上线流程多级负责人审批场景</td>\n<td>避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。</td>\n</tr>\n<tr>\n<td>2</td>\n<td><strong>命令</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/32.png\"></td>\n<td>模拟高档餐厅八大菜系,小二点单厨师烹饪场景</td>\n<td>将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。</td>\n</tr>\n<tr>\n<td>3</td>\n<td><strong>迭代器</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/33.png\"></td>\n<td>模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景</td>\n<td>提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。</td>\n</tr>\n<tr>\n<td>4</td>\n<td><strong>中介者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/34.png\"></td>\n<td>按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景</td>\n<td>用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。</td>\n</tr>\n<tr>\n<td>5</td>\n<td><strong>备忘录</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/35.png\"></td>\n<td>模拟互联网系统上线过程中,配置文件回滚场景</td>\n<td>在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。</td>\n</tr>\n<tr>\n<td>6</td>\n<td><strong>观察者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/36.png\"></td>\n<td>模拟类似小客车指标摇号过程,监听消息通知用户中签场景</td>\n<td>定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。</td>\n</tr>\n<tr>\n<td>7</td>\n<td><strong>状态</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/37.png\"></td>\n<td>模拟系统营销活动,状态流程审核发布上线场景</td>\n<td>允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。</td>\n</tr>\n<tr>\n<td>8</td>\n<td><strong>策略</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/38.png\"></td>\n<td>模拟多种营销类型优惠券,折扣金额计算策略场景</td>\n<td>定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。</td>\n</tr>\n<tr>\n<td>9</td>\n<td><strong>模板方法</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/39.png\"></td>\n<td>模拟爬虫各类电商商品,生成营销推广海报场景</td>\n<td>定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。</td>\n</tr>\n<tr>\n<td>10</td>\n<td><strong>访问者</strong></td>\n<td><img src=\"https://bugstack.cn/assets/images/2020/design/310.png\"></td>\n<td>模拟家长与校长,对学生和老师的不同视角信息的访问场景</td>\n<td>主要将数据结构与数据操作分离。</td>\n</tr>\n</tbody></table>\n<p><em>以上图稿和部分描述参考;<a href=\"https://refactoringguru.cn、https//www.runoob.com/design-pattern/visitor-pattern.html\">https://refactoringguru.cn、https://www.runoob.com/design-pattern/visitor-pattern.html</a></em></p>\n<h2 id=\"四、PDF📚下载\"><a href=\"#四、PDF📚下载\" class=\"headerlink\" title=\"四、PDF📚下载\"></a>四、PDF📚下载</h2><p>下载前,一点对原创作者的支持请求😬,<code>点赞</code>、<code>在看</code>、<code>分享</code>、<code>留言</code>、<code>赞赏</code>,完成任何一样都可以获得🉐这本PDF书籍。</p>\n<h3 id=\"1-可获得内容包括\"><a href=\"#1-可获得内容包括\" class=\"headerlink\" title=\"1. 可获得内容包括\"></a>1. 可获得内容包括</h3><ol>\n<li><code>《重学 Java 设计模式》</code>PDF 书籍一本</li>\n<li>59个对应的工程案例源码一套</li>\n<li>在线阅读版学习了资料</li>\n</ol>\n<h3 id=\"2-获取方式\"><a href=\"#2-获取方式\" class=\"headerlink\" title=\"2. 获取方式\"></a>2. 获取方式</h3><ol>\n<li>扫描下方二维码加专栏学习群,凡进群者都送书籍一本</li>\n<li>添加小傅哥微信(fustack)获取PDF书籍</li>\n<li>公众号内回复PDF下载,你会获得一个连接,打开后右侧<strong>菜单</strong> -> <code>精选</code> -> <code>值得一看的好书</code>,里面对应也有这本书籍📚</li>\n</ol>\n<p><img src=\"https://bugstack.cn/assets/images/2020/design/mkt.png\"></p>\n<h2 id=\"五、收个尾🎉\"><a href=\"#五、收个尾🎉\" class=\"headerlink\" title=\"五、收个尾🎉\"></a>五、收个尾🎉</h2><p>👣走过的路会留下足迹,👨💻码过的文会盛满四季。</p>\n<p>有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。<strong>也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈</strong></p>\n<p>本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。</p>\n<p><strong>最后,我想说</strong>:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有<code>能留下的本事</code>和<code>跳出去的能力</code>,才会相对安稳度过动荡。</p>"}],"PostAsset":[],"PostCategory":[],"PostTag":[{"post_id":"ckk9foalu0000c69l1eq87pjp","tag_id":"ckk9foam10001c69l9o3ia423","_id":"ckk9foam40004c69ldgrzdowk"},{"post_id":"ckk9foalu0000c69l1eq87pjp","tag_id":"ckk9foam30002c69lg0iu6c3w","_id":"ckk9foam50005c69lc7cr70l4"},{"post_id":"ckk9foalu0000c69l1eq87pjp","tag_id":"ckk9foam40003c69lbi7h0vq1","_id":"ckk9foam50006c69le0s428nd"},{"post_id":"ckk9gh7am0000oj9l1oav2g4o","tag_id":"ckk9foam10001c69l9o3ia423","_id":"ckk9gh7ay0002oj9lfl309mgf"},{"post_id":"ckk9gh7am0000oj9l1oav2g4o","tag_id":"ckk9gh7aw0001oj9len887zla","_id":"ckk9gh7ay0003oj9l4m06c8uh"},{"post_id":"ckk9gom3u0000t99lan01embj","tag_id":"ckk9gom440001t99l5o173d99","_id":"ckk9gom490005t99lai0eg9wv"},{"post_id":"ckk9gom3u0000t99lan01embj","tag_id":"ckk9gom480003t99l2kqfcd7z","_id":"ckk9gom490006t99l2lxh8w9g"},{"post_id":"ckk9gom470002t99l22s4hxa7","tag_id":"ckk9foam10001c69l9o3ia423","_id":"ckk9gom490007t99lgq2qcc43"},{"post_id":"ckk9gom470002t99l22s4hxa7","tag_id":"ckk9gom490004t99lg0qd11zg","_id":"ckk9gom490008t99l0afd90zz"}],"Tag":[{"name":"js","_id":"ckk9fh17x00017n9lbfst0cdq"},{"name":"coffeescript","_id":"ckk9fh17z00027n9l8v5abt80"},{"name":"java","_id":"ckk9foam10001c69l9o3ia423"},{"name":"数据结构","_id":"ckk9foam30002c69lg0iu6c3w"},{"name":"算法逻辑","_id":"ckk9foam40003c69lbi7h0vq1"},{"name":"屎山代码","_id":"ckk9gh7aw0001oj9len887zla"},{"name":"代码评审","_id":"ckk9gom440001t99l5o173d99"},{"name":"设计优化","_id":"ckk9gom480003t99l2kqfcd7z"},{"name":"设计模式","_id":"ckk9gom490004t99lg0qd11zg"}]}}