-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 178 KB
/
index.json
1
[{"content":" 这是一个“全新开始”的一个月\n回顾 琐碎流水账 这个月在工作上可以说是换了一份工作,防火墙的工作每天都很琐碎,每当你在处理这件事情的时候,就会有另一个紧急的事情需要我去定位,去解决,在各种故障和问题之间疲于奔命。现在的工作内容严重挤占了我开发编码的时间,细数一下,我这个月也就合入过一单代码😂。要知道程序员编码是很讲究编码的手感和思维的,一旦长时间地不写代码会发现原本是自己开发过的功能被其他同事合入代码后变得面目全非,而自己却总是浮于表面不参与实际代码开发,到最后我发现我开始有一些问题都无法定位了,这就很离谱了!后面我找 leader 聊了一下我的想法,说我现在的工作不是我想要的工作,已经有很长时间没有写代码了,个人是更偏向于开发的工作。leader 说知道我的诉求了,然后,然后,神奇的事情发生了,leader 给我安排需求进行开发了,但是!我原本防火墙的活还是没有减少,也就是说我在本来就很忙的情况下,又给自己揽活做,我真的😒\u0026hellip; \u0026hellip;\n输入 📚 阅读 总计阅读 1 本书,1 本读完,1 本在读\n《三体》—— Reading\u0026hellip;,三部曲第一部曲已经看完了,脑洞之大令人咋舌,看完令我对来自远方的人产生了敬意,继续读 2 部曲。 🎬 电影、电视剧 总计电影 3 部,电视剧 1 部\n电影:\n保你平安 (2022)\n推荐指数:⭐️⭐️⭐️⭐️,大鹏对偏平民化的题材拿捏的很不错,这个电影从殡葬业的视角讲述一个人为了萍水相逢的客户维权的故事。在当今互联网时代一个人随便的一句话都能让一个人的声誉瞬间支离破碎,要记得我们有说话表达的权利,但也有好好说话描述的义务,键盘的每敲下的每一个字符都有很强的力量,都有可能影响到这个星球上的某个人。 不止不休 (2020)\n推荐指数:⭐️⭐️⭐️⭐️,在这个电影中可以看到作为一个记者的蜕变成长,看到了他作为专业记者的素养,敢写敢干,也看到了在面对违背自己常识的事情上也敢于对抗权威,不为了头条而发,中国缺的就是这样的人。 铃芽之旅 すずめの戸締まり (2022)\n推荐指数:⭐️⭐️⭐️,画面宏大美丽,剧情一般,每当在念咒语都有点中二感,感觉有点出戏,个人观感,不喜勿喷。 番剧:\n模范出租车 모범택시 (2021)\n推荐指数:⭐️⭐️⭐️⭐️,在看这个剧的时候就有一种男主在惩恶扬善的感觉,一旦哪里有问题,男主一出现必能解决,大快人心,恍惚间有种看奥特曼的感觉,hh。\n🎤 播客 PodCast 上个月切换到 Pocket Casts,发现有问题:在不科学上网的情况下无法使用,有时候有些博客在开启了梯子后还是无法收听,影响收听的节奏,无奈🤷🏻♀️只能回归小宇宙了。呈现方式恢复往常。这个月收听博客时长为 32 小时,这个月收听博客的主题类型还是与 ChatGPT 相关的,因为它真的真的很火!!。\n觉得还不错的博客:\n输出 博客文章 总计 0 篇文章,读书笔记并没有\u0026hellip; \u0026hellip;\n读书笔记 0 篇 学习笔记 0 篇 ","permalink":"http://www.lmingyu.tech/posts/life/2023-3%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个“全新开始”的一个月\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2023年3月回顾"},{"content":" 这是动荡的一个月。\n回顾 琐碎流水账 这个月在工作上可以说是发生了巨变,随着去年年底美国科技巨头它们逐渐发现当业务不需太大扩展的时候,不需要很多员工也可以维持现有业务,大规模裁员就随之而至。国内的企业也开始模仿跟随,包括我司,各种裁员信息随着春节结束纷至沓来。我团队最新发生了比较大的人员调用重组,以前的老员工调走了 3 个,平时玩的比较好的小伙伴也相继离职了,团队新来了一些专家(其他组的专家)。人员的调整就会伴随着岗位工作分配的调整,而我顺利接手了以前团队防火墙的职责,日常的工作从写代码变成了,故障定位、分析、指派的“过滤器”。每天疲于应付故障处理与跟踪,相应的编码的工作以及投入的时间也随之减少。这样的工作状态,我还是不太喜欢,还没有适应过来,心里多次萌生过离职找工作的念想。组内的多个小伙伴最近也频繁地在找工作,但今年招聘市场上传统的金三银四在这个大形势下显得格外讽刺,听他们说投了几十家公司,都是死沉大海,这也慢慢打消了我逃离的想法。\n那怎么办呢,走是肯定要走的,但现在时机不对。从社会层面上,现在几乎所有企业都处于收缩招聘,甚至裁员的状态,现在找一份自己满意的工作无异于是大海捞针。从自身层面上分析,小组遭遇调整,心里肯定不太舒服或者适应的,有离开的想法也是正常的,更何况自身现在的水平去外面与这么多求职人员竞争找工作,实力还是不允许。所以综合考虑一下,还是决定学习越王勾践卧薪尝胆一段时间,好好打磨自身技术,待时机成熟另做打算,以上。\n这个月生活上的变化也挺多,重新搞了一下出租屋里的桌子,本来打算今年就不租现在这个房子了(太小了),但好在离公司挺近,骑自行车 10 分种左右的路程,目前不打算离职,所以就先再续一年吧。然后就是进行了一次比较大的断舍离,捐掉了一些买回来一直没有穿过的衣服和鞋子,整理了一下空间布局,感觉宽敞了不少,更坚定地先不搬的决定。\n输入 📚 阅读 总计阅读 0 本书,0 本读完,1 本在读\n《三体》—— Reading\u0026hellip;,原著还在看,三本的阅读量对我来说还是太多了,争取下个月回顾时能看完。 🎬 电影、电视剧 总计电影 3 部,电视剧 1 部\n电影:\n\u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️⭐️⭐️,上一部国产的体育类型片是《夺冠》,体育片的叙事给人的感觉总是跌宕起伏的,这部兵乓球从高光时刻跌落圣坛,再一步一步靠着一代代的中国运动健儿努力拼搏再重回巅峰,让人看得热血澎湃,值得推荐,可惜的就是本来是春节的排片,后面再延后,导致票房不及预期,但也不妨碍它值得好评。 \u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️⭐️,去年就看过一部黄子华主演的电影《还是觉得你最好》,作为懂粤语的人来说,还是很不错的,而这一部更是刷新了我对港片的认知,港片不仅仅是打打XX,原来律政电影也可拍的这么不错,而且悬疑部分让观影体验很不一样,反转,nice! 番剧:\n\u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️⭐️,以前看过第一季,在某一天请假之后迅速补完了,还是不错的,感觉男主是很多男生都羡慕的人呢,有自己喜欢的、有喜欢自己的、更有嫉妒自己的,但现实还是没有滴,清醒一点吧,少年! 🎤 播客 PodCast 这个月收听博客的软件彻底换成了 Pocket Casts,回顾的呈现方式与以往会不一样。总体这个软件用下来还是很不错的,就是不科学上网就加载不出来😂。这个月收听博客时长为 32 小时,这个月收听博客的主题类型主要是与 ChatGPT 相关的,因为它真的很火😁,其次就是裁员相关的,毕竟身处旋涡当中。\n觉得还不错的博客:\n536 狂飙的ChatGPT为什么能抢你饭碗 ChatGPT的出圈与大佬们的焦虑 Google裁员与公司价值观崩塌 立春024|脸书被裁,字节裸辞,于是我们决定不回大厂上班了! E76 对话远川杨天楠:我真希望!我在买房前知道这些房地产常识|房产万事屋 01 输出 博客文章 总计 1 篇文章,读书笔记并没有\u0026hellip; \u0026hellip;\n读书笔记 0 篇 学习笔记 1 篇 ","permalink":"http://www.lmingyu.tech/posts/life/2023-2%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是动荡的一个月。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2023年2月回顾"},{"content":" 用好系列就是记录自己日常如何使用好软件工具,此文为用好系列-IDEA之常用设置篇,由于公司使用的内网环境,自己平时在公司使用 IDEA 觉得还挺不错的设置无法及时同步到自己的家里,所以特意写一个系列用于记录自己日常开发编程时对这个常用的IDE所做过的改造,以提高自己的编码效率和积极性。此系列会持续更新。\n首先说明一下本文介绍 IDEA 的版本是 2022.3.2 (Ultimate Edition),电脑系统为 Mac OS 12.5 Monterry版本\nIDEA 几乎所有的设置都在详细设置菜单当中,打开设置菜单的快捷键为 ⌘ + ,\n外观 首先一款称心得手的兵器也得要悦目\n主题 个人偏向于使用暗黑的主题,用过这么多主题,还是最喜欢这个 Xcode-Dark 主题\n盗用一下这个主题插件官网的一个展示图,这种粉粉感觉还是很好看的。\n字体 对于程序员来说,自己写的代码就好像是自己的名片一样,所以一颗好看的字体也是很有必要的\n之前一直在用一个很好看的字体:JetBrains Mono,确实很好看,下面展示一下这个字体的具体代码\n但是最近又发现一款字体长在了我的审美上,它就是Fira Code,同样给出同一段代码的展示\n字体的设置在 Editor -\u0026gt; Font 里面\n这个提一个关于鼠标调节字体的设置,使用 Ctrl + 鼠标滚轮设置字体大小\n系统设置 软件相关 默认启动项 设置 IDEA 每次打开项目时的设置:\n新窗口 当前窗口 询问,我比较喜欢这个 取消自动更新 IDEA 软件本身的自动更新就不用勾选了,因为整个软件更新,容易引入兼容性文件,推荐不要选上\n插件的更新可以勾上\n调整 IDEA 的启动参数 让 IDE 运行更流畅\n在菜单找到 Help -\u0026gt; Edit Custom VM Options ,在打开的idea.vmoptions 文件中调整 -Xms 和 -Xmx等 JVM 相关参数\n-Xms2048m -Xmx8192m -XX:+UseConcMarkSweepGC -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Xms2048m:指定 JVM 堆内存的初始大小 -Xmx8192m:指定 JVM 堆内存的最大值 -XX:+UseConcMarkSweepGC :启动 Java 虚拟机时,选择 CMS 垃圾收集器作为垃圾回收器 -Djava.net.preferIPv4Stack=true :表示应该优先使用 IPv4 地址,而不是 IPv6 地址。这个参数常用在在处理网络连接,因为一些旧的网络设备可能不支持 IPv6 地址,如果默认情况下使用 IPv6 地址,可能会导致连接不可用。通过设置该参数为 true,可以确保 Java 在处理网络连接时优先使用 IPv4 地址,从而避免连接不可用问题 -Dfile.encoding=UTF-8 :文件编码使用 UTF-8 编码格式 项目编码相关 显示代码行号与方法分割符 代码智能提示功能 代码提示和补充有一个特性:区分大小写。建议去掉勾选,这样你不管输入大写还是小写都能出现代码提示\n项目文件编码设置为 UTF-8 有时候代码的错误就有可能是代码编码引起的,所以项目在开发前统一编码格式很有必要\n设置 Java 文件头注释信息 我们在创建新的 Java 文件的都免不了需要在这个文件头部写上注释,用于说明这个文件的一些基本信息,比如说创建人、创建时间、描述信息等等,但每次都自己手动输入就很麻烦,所以 IDEA 给出了创建文件时的模板,我们可以自定义属于自己的模板\n由于我是 Java 的开发者,所以只设置了 class、interface、Enum 这 3 种文件,关于注释中的 ${USER} 是预设的变量,其他的变量可以在官网查看\n#if (${PACKAGE_NAME} \u0026amp;\u0026amp; ${PACKAGE_NAME} != \u0026#34;\u0026#34;)package ${PACKAGE_NAME};#end #parse(\u0026#34;File Header.java\u0026#34;) /** * * @author ${USER} * @date ${YEAR}/${MONTH}/${DAY} * **/ public class ${NAME} { } #if (${PACKAGE_NAME} \u0026amp;\u0026amp; ${PACKAGE_NAME} != \u0026#34;\u0026#34;)package ${PACKAGE_NAME};#end #parse(\u0026#34;File Header.java\u0026#34;) /** * * @author ${USER} * @date ${YEAR}/${MONTH}/${DAY} * **/ public interface ${NAME} { } #if (${PACKAGE_NAME} \u0026amp;\u0026amp; ${PACKAGE_NAME} != \u0026#34;\u0026#34;)package ${PACKAGE_NAME};#end #parse(\u0026#34;File Header.java\u0026#34;) /** * * @author ${USER} * @date ${YEAR}/${MONTH}/${DAY} * **/ public enum ${NAME} { } 设置自动编译 设置自动编译的好处:\n能够节省时间,不需要手动编译 可以及时发现代码中的语法错误,避免在运行代码时出现问题 设置 Maven 的 Reload 操作 当 pom.xml 文件发生改动变更的时候,会自动触发 Maven 的 Reload 操作\n这个文章应该会持续更新,只要我还在用 IDEA 这个软件 ♪(・ω・)ノ\n","permalink":"http://www.lmingyu.tech/posts/tech/%E7%94%A8%E5%A5%BDidea-2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e用好系列就是记录自己日常如何使用好软件工具,此文为用好系列-IDEA之常用设置篇,由于公司使用的内网环境,自己平时在公司使用 IDEA 觉得还挺不错的设置无法及时同步到自己的家里,所以特意写一个系列用于记录自己日常开发编程时对这个常用的IDE所做过的改造,以提高自己的编码效率和积极性。此系列会持续更新。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"用好系列-IDEA之常用设置篇"},{"content":" 2023 新的一年,第一个回顾姗姗来迟\n回顾 琐碎流水账 随着 2022 年底的放开,不仅马路上的车辆变多了,街道上的人流也变多了,就像那句话:\n从前的生活慢,如今车水马龙。\n过年前几天心思完全放飞,心系放假。把手上紧剩要交付的任务搞完之后,早早就提交了放假的申请。在节前请了 5 天的年假,加上前面的 2 天周末和春节,总共 14 天的春节就开始了,爽(^▽^)😄!前几年由于疫情,更多的人选择原地过年,造成了很多人已经有 3 年没有回家过年。由于政策的放开,加上今年的过年感觉比往常来的更早,越来越多的人都踏上了回家的路,以前春运人挤人的感觉马上浮现出来了。\n回到家之后,时间就过得很快,一系列过年必须做的事情,如约而至。年前的大扫除清洁(一家人忙碌一整天把家里里里外外都打扫清理了一遍)、买春联贴、逛花街(除夕当天去买年花,把家里布置的红红火火)、一家人团团圆圆地吃年夜饭(必须剩饭,寓意年年有余😂)、初一去寺庙祭拜(祈求新的一年全家人身体健康)、顺峰山公园绕大圈(广东人新年第一天“行大运”)、一家人去看电影《流浪地球 2》(120 帧 CINITY 巨幕厅,视觉与听觉都是全新的体验,票价也是真贵,80 一张,但还是推荐看 CINITY 的,会有不一样的观影体验)、在家附近到处看和逛(去了一天的动物园)、无休止的躺着(春节必备的活动😄)。看起来做了很多事情,但是并没有出现很多出乎我意料的事情(没有出远门等等吧),导致今年过年并没有什么特别的记忆点,这年过得稀里糊涂的,O(∩_∩)O哈哈~,明年争取过一个更好的春节。下面就放一些这一个月拍的照片,纪念一下吧。\n新买的 Switch 游戏——喷射战士 3,一改我以前对射击游戏的印象,它并不是以杀死敌方为获胜点。而是以往对方喷射的涂料面积来算获胜方,玩起来的感觉还不错,推荐。 大扫除之后的大吃一顿 除夕夜守岁的汤圆 豚鼠 羊驼 在家的这几天,我顺便更新了一下我的信息流和工作流,比如将做笔记的软件从 Notion 换成了 Obsidian,文字的输入源从Reeder 5 换成了 Readwise Reader,视频主要的来源正在努力从 B 站转成 YouTube,音频来源 APP 从小宇宙切换到了 Pocket Casts。等以后信息流稳定之后再总结写一下自己的输入与输出流。\n输入 📚 阅读 总计阅读 0 本书,0 本读完,1 本在读\n《三体》—— Reading\u0026hellip;,最近有关于刘慈欣的热度非常高,无论是春节档的《流浪地球 2》,还是《三体》电视剧都将中国科幻已很不错的印象呈现在我们的眼前。所以花点时间阅读一下原著还是很有必要的。 🎬 电影、电视剧 总计电影 3 部,电视剧 1 部\n电影:\n\u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️⭐️⭐️,今年春节档的王者,无论是画面和情节,在众多的电影中都独树一帜。个人的感受是非常值得一看,特别是要选 CINITY 厅。 \u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️,喜剧与悬疑结合还是显得不是很搭,最后升华的背诵满江红根据有点突兀,不推荐,但是不是很懂票房为啥是最高的 \u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️,画面色彩很鲜艳,但是有点过于鲜艳,剧情有点压抑,看完不是很舒服,所以生活还是要多笑一下,地球还是很美好的。 电视剧:\n\u003c!DOCTYPE HTML\u003e 推荐指数:⭐️⭐️⭐️⭐️,宋慧乔的演技还是很不错的,被校园暴力阴影笼罩的她决定复仇,所有的事情都在她的计划当中,复仇得手的几个瞬间简直太爽了,推荐,期待第二季 🎤 播客 PodCast 这个月收听博客时长为 33 小时 12 分钟,这个月春节在家过节本以为会少听很多博客,没想到也听了有 33 个小时,这个月听的最多的节目《ByteTalk》讲述了一些程序员的过往以及一些博客以前的节目。\n觉得还不错的博客:\n输出 博客文章 总计 0 篇文章,这个月有的不尽如人意,下个月要努力\n读书笔记 0 篇 学习笔记 0 篇 项目 暂无进展\n","permalink":"http://www.lmingyu.tech/posts/life/2023-1%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e2023 新的一年,第一个回顾姗姗来迟\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2023年1月回顾"},{"content":" 回顾这一年发生的点点滴滴\n第一次写年度总结,不知道从哪里开始,今年建设这个网站是本意也是想着能有一个地方记住自己做过的事,想过的想法,能与他人交流观点。从6 月建站以来,半年时间总共写了19 篇文章,说多不多,希望在明年能够坚持,多记录,加油💪🏻。\n生活 回顾今年,贯穿整年的主题就是:疫情,从年初过年之后就经历了一段时间的在家办公,长时间的在家办公,也让我下了决心买了一个人体工学椅,要知道程序员是一个长时间坐着工作的工种。深圳刚可以正常上班,然后上海就开始维持了 3 个月的封城,而我们从一开始的 72 小时加码到48 小时,最后到了 6 月开始就已经是 24 小时,然后就开始了半年的每天核酸检测。每天的工作下班后还有惦记着核酸还没做\u0026hellip; 虽然说我已经习惯了,甚至已经养成了习惯,脱口罩,张嘴,戴上口罩,一套动作形成了肌肉记忆。\n时间来到 12 月份,国家政策大转变,核酸已经不验,而且阳性也不需要隔离。造成的结果就是病毒大面积的扩散传播,公司的人陆陆续续的中招,我最近也得了新冠,抗原阳性,发烧、流涕、咳嗽,最典型的症状都囊括了,发烧到 39 度的感觉太难受了,年轻人都这么难受了,上了年纪有基础疾病的长辈们该咋办呢。\n持续一整年的核酸检测、封锁终于是告一段落了,但是我们与病毒的正面交锋才刚刚开始,希望明年我们能刚好吧!\n工作 今年可以说是我成为正式程序员的第一年(去年主要处于实习阶段,工作内容也是一些与代码无关的事情)。在这一年,我在团队内从一个新兵,通过一个个需求,一次次的交付,逐渐对团队的运作,工作环境的操作,代码的编写都越来越熟练,上半年从 Scala 转变到 Java,虽说我上手 Scala 也不久,但是转换语言调整也比较大,主要是团队内部整体代码架构框架都发生了大改。但我还是快速的转换并适应,迅速交付工作。而且最近,在年底的项目交付当中,因为团队内的架构师受伤,我充当了一回项目的负责人,感受到了统筹工作的难处与难度,从这里面也学到了很多东西。\n总体来说,我对今年我在职业上的成长还是很满意的,希望明年自己能够在知识的深度上更上一层。\n输入 编程 今年在编程的输入可以说什么都想学,什么都学不精,什么都略懂皮毛,一深入就束手无策,我也知道学习一门技术最好的方式就是一步一步跟着书敲一遍,但是我就很害怕沉没成本太大,心里想着花过多时间投入,会不会周期太长了,万一学完以后不用怎么办呢,诸如此类的想法每次都能打断我的学习进度,最终学到永远都是前几章的东西,后面的东西几乎没有沾染。\n欲速则不达,慢才是真正的快。希望自己明年能明白这一点吧。\n书 今年,我在书本的阅读上可以算的上我近几年之最,虽然数量也是少的可怜,但是读书不是数量取胜,是要与作者进行一次次深度的交流,理解他的想法。而且今年还购入了一个书架,一共 5 层,3 层放了书,这 3 层里面有 2 层是编程相关的书籍,剩下一次是什么类型都有的书籍。\n总结就是,书开始读了,但还是得培养自己读书的兴趣。\n影 今年由于疫情的原因,去电影院看电影的次数屈指可数,进而使用家庭影院进行替代,加上最近使用了阿里云盘 + WebDAV(在阿里云服务器上使用 docker 构建的一个服务) + infuse 搭建起来自己影视剧库,还有今年购入的 Apple Studio Display 显示器(音响效果还是很不错的),这一套装备,在家看电影简直太爽了。\n音 音可以分为音乐和音频\n音乐🎵的话,今年听歌的平台从网易云切换到了 Spotify ,虽然很多这个平台没有之前听歌的痕迹(就是暂时不会推荐你想听的歌),但是拥有了很多听歌的资源,也扩展了自己听歌的类型,感觉很不错。\n音频的话,今年多了一个爱好,就是听播客。听播客的行为现在看起来好像很复古,像极了爸妈那个年代会用收音机📻的行为。听播客很大的一个好处是陪伴不打扰,吃饭可以听,骑车可以听,锻炼可以听,甚至洗澡也可以听,它真的可以做到随时随地。那听这么多有什么用呢,先不说听的内容有没有营养,光论这个信息的输入时间就可以秒杀很多视频课程。今年我就是在上述的那些场景,听了总共 253 个小时,这个长度比很多视频教程都要长。而且听博客内容也可以涉猎很多,不要说学到很多东,就不要带着要学东西来听,就当成碎片时间的合集,比刷短视频要好上不少,把每一天中的暗时间(刘未鹏《暗时间》)积攒下来,也是一大笔财富。更何况播客的延续性很不错,就是上午听过的地方,下班继续听个几分钟就能马上接上。\n所以播客这个爱好可以继续好好培养一下。\n展望 希望明年工作上能顺顺利利,编程技术上能更上一层楼,身体能够健健康康,爱好上能更加丰富和坚持,最好希望后疫情时代会变得越来越好吧,世界能够和平,以上!\n","permalink":"http://www.lmingyu.tech/posts/life/2022-%E5%B9%B4%E5%BA%A6%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e回顾这一年发生的点点滴滴\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2022 年度回顾"},{"content":" 这个月的总结就是非常忙,临近年底交付,项目遗留下的工作非常多,每天都在加班,最长连续上班 13 天,最终还是赶在 30 号这一天完成了团队定下的目标,后面的任务就是对系统进行修修补补了。虽然任务是完成了,但是这个月对于自身的输入与输出缺少之又少,既然没啥可说的,简单地总结回顾一下吧,争取下个月能有所改善。\n输入 📚 阅读 总计阅读 2 本书,2 本读完,2 本在读\n《暗时间》—— Reading\u0026hellip;,找时间好好读一下 《置身事内》—— Reading\u0026hellip;,这本书讲述了中国经济政策的由来以及发展历程,详细的描述了政府与市场的关系,还是很值得一看的,可以了解当今的政策背后的原因。 🎬 电影、电视剧 总计电影 0 部,电视剧 1 部,这个月使用了infuse整理了自己的影音库,通过阿里云盘作为自己的仓库,感觉还是很像那么回事的。\n电影:无 电视剧: \u003c!DOCTYPE HTML\u003e 用了某个周末的 2 天熬夜看完了 2 季,逐渐感受到了美剧的魅力。看剧的过程当中一方面很期待未来的技术真的能做到这个程度,这就代表人类的科技技术已经达到了一个很高的水平,但一方面又在思考这样的技术如果被滥用,该会是多么的恐怖 🎤 播客 PodCast 这个月收听博客时长为 37 小时 26 分钟,这个月印象比较深刻的博客是声动早咖啡,原因是这个博客每周一、三、五早上都会更新最新的一期博客,可以让我在忙碌的工作生活中能知道当前是周几,离周末还有多远😂。\n觉得还不错的博客:\n输出 博客文章 总计 1 篇文章\n读书笔记 0 篇 Spring 学习笔记 1 篇 项目 暂无进展\n","permalink":"http://www.lmingyu.tech/posts/life/2022-11%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这个月的总结就是非常忙,临近年底交付,项目遗留下的工作非常多,每天都在加班,最长连续上班 13 天,最终还是赶在 30 号这一天完成了团队定下的目标,后面的任务就是对系统进行修修补补了。虽然任务是完成了,但是这个月对于自身的输入与输出缺少之又少,既然没啥可说的,简单地总结回顾一下吧,争取下个月能有所改善。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2022年11月回顾"},{"content":" Spring 的学习笔记——创建 Bean 的方式,本文代码存放在 github 仓库中\n什么是 Bean JavaBeans:\nJavaBeans 是 Java 中一种特殊的类,可以将多个对象封装到一个对象(Bean)中。\n特点是:可序列化、提供无参构造器、使用 Getter、Setter 方法访问对象的属性\nSpring 容器沿用了 Java 的规则,将容器中管理的可重用的组件称为 Bean。\n容器会根据所提供的元数据来创建并管理这些 Bean,其中也包括管理 Bean 之间的依赖关系\nBean 的定义会包含以下信息:\nBean 的名称一般是 Bean 的 id Bean 的具体类信息是一个全限定的类名 Bean 的作用域 单例(singleton):每次获取返回的是同一个 Bean 原型(prototype):每次获取返回的是一个新的对象 依赖注入的相关信息,如构造方法参数、属性以及自动注入(autowire)方式 创建销毁的相关信息,如懒加载模式,初始化回调方法与销毁回调方法 容器的创造 如果我们想搞清楚 Bean 是如何创建的,首先得找到创建 Bean 的人——也就是 Spring 容器(创建和管理 Bean 的地方)\n在 Spring 容器中起到关键作用的就是这 2 个角色: BeanFactory、ApplicationContext,这 2 个都可以代表 Spring 容器\nBeanFactory 定义 BeanFactory 是创建、管理各种类对象的通用工厂\nBeanFactory 接口是 IoC 容器中最基础的接口,在 Spring 中有多种实现,最常用的是 XmlBeanDefinitionReader、DefaultListableBeanfactory,通过 BeanFactory 的 getBean() 方法可以把 Bean 创建出来\n示例 Car: 要被创建的 Bean\n/** * 车 * @author liangtaiming * @date 2022/10/30 **/ public class Car { /** * 品牌 */ private String brand; /** * 颜色 */ private String color; /** * 空参构造 */ public Car() { } /** * 有参构造 */ public Car(String brand, String color) { this.brand = brand; this.color = color; } /** * 开车 */ public void drive() { System.out.println(\u0026#34;Drive the \u0026#34; + color + \u0026#34; \u0026#34; + brand + \u0026#34;\u0026#39;s Car.\u0026#34; ); } } bean.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;bean id=\u0026#34;car\u0026#34; class=\u0026#34;Car\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;Tesla\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;White\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;/beans\u0026gt; Test.java\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; /** * @author liangtaiming * @date 2022/10/30 **/ public class Test { public static void main(String[] args) { // 搜索类加载路径下的 beans.xml 文件创建 Resource 对象 ClassPathResource classPathResource = new ClassPathResource(\u0026#34;beans.xml\u0026#34;); // 创建出默认的 BeanFactory 容器 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 让默认的 BeanFactory 容器加载 classPathResource 对应的 XML 配置文件 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(classPathResource); System.out.println(\u0026#34;----- init BeanFactory -----\u0026#34;); Car car = beanFactory.getBean(\u0026#34;car\u0026#34;, Car.class); System.out.println(\u0026#34;----- car bean is ready for use -----\u0026#34;); car.drive(); } } 输出结果\n----- init BeanFactory ----- ----- car bean is ready for use ----- Drive the White Tesla\u0026#39;s Car. 这个过程中\n首先XmlBeanDefinitionReader 通过 Rsesource 装载 Spring 配置信息并启动 IOC 容器 然后再通过BeanFactory 方法从 IOC 容器中获取 Bean ApplicationContext ApplicationContext 是由 BeanFactory 派生而来,提供了更多面向实际应用的功能,在大多数的情况下,都是使用 ApliacationContext 实例作为 Spring 容器\nApplicationContext 的初始化与 BeanFactory 的初始化类似\n如果 配置文件在类路径下,可以优先使用 ClassPathXmlApplicationContext 实现类 如果 配置文件在文件系统路径下,可以优先使用 FileSystemXmlApplicationContext 实现类 示例 Test.java\npackage tech.mingyu; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author liangtaiming * @date 2022/10/30 **/ public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(\u0026#34;beans.xml\u0026#34;); System.out.println(\u0026#34;----- init BeanFactory -----\u0026#34;); Car car = applicationContext.getBean(\u0026#34;car\u0026#34;, Car.class); System.out.println(\u0026#34;----- car bean is ready for use -----\u0026#34;); car.drive(); } } 输出结果\n----- init BeanFactory ----- ----- car bean is ready for use ----- Drive the White Tesla\u0026#39;s Car. 常见的 ApplicationContext 实现 类名 说明 classPathXmlApplicationContext 从 CLASSPATH 中加载 XML 文件来配置 FileSystemXmlApplicationContext 从文件系统中加载 XML 文件类配置 AnnotationConfigApplicationContext 根据注解和 Java 类配置 BeanFactory 与 ApplicationContext 的区别 ApplicationContext 支持 BeanFactory 全部功能,使用起来更方便,而且还具备额外的功能:\n初始化 BeanFactory 在初始化容器时,并没有实例化 Bean ,只有在第一次访问某个 Bean 时才实例化 Bean ApplicationContext 会默认在初始化应用上下文时就实例化所有单例(singleton) Bean,因此 ApplicationContext 的初始化耗时会比 BeanFactory 稍长一些,但程序后面获取实例 Bean 时就会有较好的性能也可以通过配置取消预初始化 提供国际化支持 资源访问 事件机制 同时加载多个配置文件 建议优先使用 ApplicationContext,只有对某些内存非常关键的应用,才考虑使用 BeanFactory\n容器创建好了,就可以通过容器创建出对象 Bean\n获取 Bean 的方式 通过Bean id 获取 Cat.java\npackage tech.mingyu; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author liangtaiming * @date 2022/11/20 **/ @NoArgsConstructor @AllArgsConstructor @Data public class Cat { private String name; private String age; public void sayHello() { System.out.println(\u0026#34;The \u0026#34; + name + \u0026#34; Cat is \u0026#34; + age + \u0026#34; years old!\u0026#34;); } } beans.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;bean id=\u0026#34;beanOne\u0026#34; class=\u0026#34;tech.mingyu.Cat\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;kitty\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;/beans\u0026gt; Test.java\npackage tech.mingyu; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author liangtaiming * @date 2022/11/20 **/ public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(\u0026#34;beans.xml\u0026#34;); // 通过 Bean id 获取对象 Cat cat = (Cat) applicationContext.getBean(\u0026#34;beanOne\u0026#34;); cat.sayHello(); } } 输出结果\nThe kitty Cat is 3 years old! 在 Spring 容器中,id 是唯一标识,可以通过 Bean id 来确定并创建出唯一的 Bean 对象,但是这个 Bean id 是不允许重复的,否则会报异常\n通过类型获取 除了 id ,还可以通过 Bean 类型创建出 Bean 对象\n如下:\npublic class Test { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(\u0026#34;beans.xml\u0026#34;); // 通过 Bean id 获取对象 Cat cat = (Cat) applicationContext.getBean(\u0026#34;beanOne\u0026#34;); cat.sayHello(); System.out.println(\u0026#34;----- 分割线 -----\u0026#34;); // 通过 Bean 类型获取对象 Cat cat2 = applicationContext.getBean(Cat.class); cat2.sayHello(); } } 输出结果\nThe kitty Cat is 3 years old! ----- 分割线 ----- The kitty Cat is 3 years old! 可以看出输出结果都是一样的,那这两种方式有什么区别呢?\n第一种情况:假设出现两个 Bean 的类型都一样的情况,那么采用 Bean 类型创建 Bean 就会导致报错:NoUniqueBeanDefinitionException: No qualifying bean of type 'tech.mingyu.Cat' available: expected single matching bean but found 2: beanOne,beanTwo ,这是因为同类型的 Bean 存在 2 个及以上,Spring 容器在初始化的时候无法分辨出哪一个 Bean 是需要被创建出来的。 \u0026lt;bean id=\u0026#34;beanOne\u0026#34; class=\u0026#34;tech.mingyu.Cat\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;kitty\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;beanTwo\u0026#34; class=\u0026#34;tech.mingyu.Cat\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;Bady\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; 输出结果\nThe kitty Cat is 3 years old! ----- 分割线 ----- Exception in thread \u0026#34;main\u0026#34; org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type \u0026#39;tech.mingyu.Cat\u0026#39; available: expected single matching bean but found 2: beanOne,beanTwo at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1273) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:494) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) at tech.mingyu.Test.main(Test.java:21) 通过 Bean id 和 类型获取 Bean 如果一个类型的 Bean 有多个,而且非要通过对象类型获取 Bean,就可以通过第三种方式获取\nTest.java\n// 同时通过 Bean id 与 类型获取对象 Cat cat = applicationContext.getBean(\u0026#34;beanTwo\u0026#34;, Cat.class); cat.sayHello(); 输出结果\nThe Bady Cat is 5 years old! 通过接口或类获取Bean 如果你需要获取的 Bean 实现了某个接口,获取继承了某个类,可以将获取的 Bean 可以直接设置为接口的类型或父类的类型\n示例\nDog.java\npackage tech.mingyu; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author liangtaiming * @date 2022/11/20 **/ @NoArgsConstructor @AllArgsConstructor @Data public class Dog extends Animal{ private String name; private String age; public void sayHello() { System.out.println(\u0026#34;The \u0026#34; + name + \u0026#34; Dog is \u0026#34; + age + \u0026#34; years old!\u0026#34;); } } Animal.java\npackage tech.mingyu; import lombok.Data; import lombok.NoArgsConstructor; /** * @author liangtaiming * @date 2022/11/23 **/ @NoArgsConstructor @Data public class Animal { private String gender; public void eat() { System.out.println(\u0026#34;Animal is eating something!\u0026#34;); } } beans.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;bean id=\u0026#34;beanOne\u0026#34; class=\u0026#34;tech.mingyu.Cat\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;kitty\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;beanTwo\u0026#34; class=\u0026#34;tech.mingyu.Cat\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;Bady\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;beanThree\u0026#34; class=\u0026#34;tech.mingyu.Dog\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;jack\u0026#34;/\u0026gt; \u0026lt;constructor-arg index=\u0026#34;1\u0026#34; value=\u0026#34;10\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;/beans\u0026gt; /** * @author liangtaiming * @date 2022/11/20 **/ public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(\u0026#34;beans.xml\u0026#34;); // 通过父类获取 Bean Animal animal = applicationContext.getBean(Animal.class); animal.eat(); } } 输出结果\nAnimal is eating something! 从这个可以看到,Dog 类在 beans.xml 中配置了对应类型的 Bean ,而 Animal 类并没有,但由于 Dog 类继承了 Animal 类,所以可以直接获取父类的 Bean 的对象,接口也是同样的道理。\n但是这种做法是有前提的:就是子类的 Bean 必须唯一,如果是 Cat 类继承了 Animal 类的话,在获取 Animal Bean 的时候就会报错Exception in thread \u0026quot;main\u0026quot; org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'tech.mingyu.Animal' available: expected single matching bean but found 2: beanOne,beanTwo\n参考资料 \u003c!DOCTYPE HTML\u003e \u003c!DOCTYPE HTML\u003e ","permalink":"http://www.lmingyu.tech/posts/tech/spring-%E5%88%9B%E5%BB%BAbean%E7%9A%84%E6%96%B9%E5%BC%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003eSpring 的学习笔记——创建 Bean 的方式,本文代码存放在 \u003ca href=\"https://github.com/lmingyul/springlearn\"\u003egithub 仓库\u003c/a\u003e中\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Spring-创建Bean的方式"},{"content":" 因为有十一假期的原因,这个 10 月过的特别快,新的一次回顾又来了。回顾这个 10 月,因为邻近项目交付时间,工作变得越来越忙了,不仅需要交付需求,还需要带着新人上手做新项目,这个月在输入与输出总体看还是比较少的,希望能逐渐找到工作与生活的平衡吧\n输入 📚 阅读 总计阅读 4 本书,2 本读完,2 本在读\n《你是你吃出来的》 《我是一只IT小小鸟》, 这本是在国庆期间读完了,通篇读完可以找到一些同感吧 《暗时间》—— Reading\u0026hellip;,这本看着很不错,但是没时间静下心来阅读 《我们为什么要睡觉》—— Reading\u0026hellip;,这本每次都读不完😂 🎬 电影、电视剧 总计电影 2 部,电视剧无\n电影: \u003c!DOCTYPE HTML\u003e 故事发生在日本的一个列车上,可以将多个刺杀故事线进行串联,男主每一次都能化险为夷,只能说善有善报,恶有恶报,电影中途混入了一条非洲毒蛇,全程害怕😱,担心什么时候会跑出来。总体来说电影比较诙谐幽默,还是比较推荐观看的 \u003c!DOCTYPE HTML\u003e 故事背景是第一次世界大战,男主为了向母亲证明自己,擅自报名上战场,故事线就开始了\u0026hellip; 电影没有太多战争血肉模糊的镜头,更多的描述了男主人公与他们的伙伴的感情发展,但因为战争他的伙伴纷纷牺牲。战争是无情的,是某些人维“权”争“权”的工具,但士兵与人民是无辜的,因为将军的面子,男主最后也倒在了黎明之前。最近世界格局的不稳定,说不定那天就会发生在你我身边,还是需要这部电影能给大家留下战争原本的样子吧,愿世界和平! 电视剧——无 🎤 播客 PodCast 这个月收听博客时长为 36 小时34 分钟\n觉得还不错的博客:\n输出 博客文章 总计 5 篇文章\n读书笔记 3 篇 Spring 学习笔记 1 篇 项目 暂无进展\n","permalink":"http://www.lmingyu.tech/posts/life/2022-10%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e因为有十一假期的原因,这个 10 月过的特别快,新的一次回顾又来了。回顾这个 10 月,因为邻近项目交付时间,工作变得越来越忙了,不仅需要交付需求,还需要带着新人上手做新项目,这个月在输入与输出总体看还是比较少的,希望能逐渐找到工作与生活的平衡吧\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2022年10月回顾"},{"content":" 我们该怎么吃呢?\n\u003c!DOCTYPE HTML\u003e 🚀 三句话总结本书 以医生的视角从生物到医学讲述了我们应该怎么吃 文章以真实例子与建议穿插的形式进行叙述,具有说服力 给出更适合中国人的食谱 🎨 简单总结和感受 粗略翻阅一下,很多东西与我的认知不太符合,比如说每天至少2 个鸡蛋+300 毫升牛奶,这个我感觉量太多了,每周吃 2 ~ 3 次鱼,这个我更加做不了,每天都在食堂吃的我并不能觉得食堂的菜中是否会有鱼\n,引用一句话:很多道理我都懂,但我依旧过不好我这一生\n☘️ 为什么我要读这本书 了解生活当中的饮食对我们的意义,从饮食开始,改善一下程序员不健康的生活\n✍️ 最精华的3句书摘 钙的最好来源是奶和奶制品,每天最好喝 300 毫升的牛奶 世界上最好的药:早餐、午餐和晚餐 📒 书摘 体力劳动者要多吃碳水化合物,具体数量根据工作性质和运动量决定。脑力劳动者同时运动少的人吃的粮食量要少,但每天要保证 150 克的粮食。正在长身体的少年儿童要多吃粮食,而老年人要相应减少 平时可以少吃多餐,多吃复合型碳水化合物(天然的食物都是复合型的,如土豆、燕麦、莲藕等),少吃蔗糖和精米、精面 脂肪是脂溶性维生素的载体。维生素 A、D、E、K等脂溶性维生素,只有在有油脂的环境下才能够被吸收。因此,不吃油脂的人常会出现脂溶性维生素不足等症状 一般来说,每天吃一个鸡蛋、一袋牛奶、100 ~ 150 克瘦肉,每周吃 2 ~ 3 次鱼,摄入的动物脂肪量基本就够用了 一个程序员 185 的身高,平时生活和工作几乎 75% 的时间都是坐着,25% 的时间是站着,这种类型的人属于轻体力劳动者,那么他每天所需的总能量应该是 (185 - 105) * 30 = 2400 千卡, 各种维生素: 维生素 A 缺乏症状:夜盲、干眼、慢性咽炎 补充 :动物肝脏、奶油、鸡蛋… 维生素 D 缺乏症状:骨质疏松、高血压、抑郁、盗汗,容易感冒等 补充 :动物肝脏、鸡蛋、牛奶、三文鱼、动物骨头等,晒太阳很重要 维生素 B 族 缺乏症状:上火、记忆力下降、消化不良等 补充:动物内脏、奶、蛋、绿叶蔬菜、坚果 维生素 C 缺乏症状:牙龈出血、贫血、心衰等 补充:新鲜水果蔬菜、一个人一天最好吃 3 种以上的水果,合起来半斤以上 微量元素在体内虽然需求很少,但其生理剂量与中毒剂量比较接近,摄入过多易产生毒性,矿物质千万不要乱补 钙的最好来源是奶和奶制品,每天最好喝 300 毫升的牛奶 补充膳食纤维:每人一天最好吃 1 斤蔬菜,其中叶菜最好占一半,水果最好连皮吃,主食应该选择全谷类、薯类、根茎类,少吃精米精面和精加工制品,如面包、蛋糕、饼干等 一个人每日摄水量总和约 2500 毫升,摄入来源主要为: 饮用水,大概 1200 毫升 食物,大概 1000 毫升 物质代谢,葡萄糖产生能量分解产生的水,大约 300 毫升 怎么判断喝水量,看是否口渴,以及看尿的颜色 地中韩式饮食:被人们代指健康、简单、清淡以及富含营养的饮食,食物构成如下: 粮食类:以五谷杂粮为主,包括全麦、玉米、土豆、豆类、薯类、根茎类等 蔬菜类:新鲜蔬菜,西红柿、洋葱等 水果类:各种新鲜水果,如柠檬、葡萄、蓝莓等 蛋白类:鱼,每周吃一些瘦的畜禽类的肉,每天都会有鸡蛋,牛奶及其制品 油类:橄榄油、坚果中的油 世界上最好的药:早餐、午餐和晚餐 早餐: 2 个鸡蛋+300 毫升牛奶,或者再加一点肉类 全麦食品、老玉米,各种薯类,或者带馅的食物:包子、饺子和肉夹馍 一小把坚果 水果:苹果、香蕉、黄瓜 ","permalink":"http://www.lmingyu.tech/posts/input/%E4%BD%A0%E6%98%AF%E4%BD%A0%E5%90%83%E5%87%BA%E6%9D%A5%E7%9A%84%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003e我们该怎么吃呢?\u003c/p\u003e\n\u003c/blockquote\u003e","title":"《你是你吃出来的》读书笔记"},{"content":" Spring 的学习笔记——依赖注入,本文代码存放在 github 仓库中\n什么是依赖注入 什么是依赖? 在我们 Java 编程过程当中,总会有遇到 A 类对象实例需要调用 B 类对象方法的情形(除非你一个类走天下,否则总会遇到这种情况的),这种情况被 Spring 称为依赖,A 对象要用到 B 对象,即 A 对象依赖 B 对象,这种关系被称为依赖关系\n现在知道什么是依赖,那依赖注入是什么呢\n先看一下依赖的情况,我们是怎么处理的\n一般做法 当某个 Java 对象(调用者)调用另一个 Java 对象(被调用者)的方法时,一般的做法是调用者主动创建被调用者,然后再调用被调用者的方法\n我们创建两个类来模拟游戏,一个人 Person 类,一个 武器:剑 Sword 类\npackage com.mingyu.javalearn; /** * 剑 * @author liangtaiming * @date 2022/10/16 **/ public class Sword { /** * 攻击力 */ private String firePower; /** * 空参构造 */ public Sword() { } /** * 有参构造 */ public Sword(String firePower) { this.firePower = firePower; } /** * 攻击敌人方法 */ public void attackEnemy(String enemyName) { System.out.println(\u0026#34;Sword can damage enemy \u0026#34; + enemyName + \u0026#34; \u0026#34; + this.firePower + \u0026#34; life\u0026#34;); } } /** * 人 * @author liangtaiming */ @AllArgsConstructor @Data public class Person { public void attack(String enemy){ Sword sword = new Sword(\u0026#34;50\u0026#34;); sword.attackEnemy(enemy); } } 测试类\npublic class Test { public static void main(String[] args) { Person mingyu = new Person(); mingyu.attack(\u0026#34;jack\u0026#34;); } } 输出结果:\nSword can damage enemy jack 50 life 这种方法是我们最常用解决对象间依赖的方法,人 Person 类需要 new Sword()创建出武器对象,再调用武器对象的attackEnemy() 方法\n这样的做法虽然简单,但也有不足的地方:\n扩展性差:如果后期需要换武器,不用剑了换成使用其他武器,就需要对人 Person 这个类的代码进行修改,这样会导致人这个调用这个与被依赖对象剑产生了硬编码耦合,这样不利于代码的维护与改造。 对象间职责不明确:对于人 Person 对象来说,它只关心调用武器的攻击方法,并不关心武器的制作过程,但在这种一般模式下,人就直接参与到武器的制作,导致职责不明确 简单工厂方法 调用者先找到能创建被依赖的对象的工厂,然后主动通过工厂去获取被依赖的对象,最后再调用被依赖对象的方法\n可以看一下下面这个例子\n/** * 武器接口 * @author liangtaiming * @date 2022/10/16 **/ public interface Weapon { /** * 攻击敌人方法 * @param enemyName 敌人名称 */ public void attackEnemy(String enemyName); } /** * 剑 * @author liangtaiming * @date 2022/10/16 **/ public class Sword implements Weapon { /** * 攻击力 */ private String firePower; /** * 空参构造 */ public Sword() { } /** * 有参构造 */ public Sword(String firePower) { this.firePower = firePower; } /** * 攻击敌人方法 */ @Override public void attackEnemy(String enemyName) { System.out.println(\u0026#34;Sword can damage enemy \u0026#34; + enemyName + \u0026#34; \u0026#34; + this.firePower + \u0026#34; life\u0026#34;); } } /** * 枪 * @author liangtaiming * @date 2022/10/17 **/ public class Gun implements Weapon { /** * 攻击力 */ private String firePower; /** * 无参构造 */ public Gun() { } /** * 有参构造 */ public Gun(String firePower) { this.firePower = firePower; } @Override public void attackEnemy(String enemyName) { System.out.println(\u0026#34;Gun can damage enemy \u0026#34; + enemyName + \u0026#34; \u0026#34; + this.firePower + \u0026#34; life\u0026#34;); } } 再创建一个工厂类WeaponFactory,根据调用者不同的要求,创建出不同的武器对象并返回。而如果碰到不合法的要求,会返回一个Runtime异常\n/** * 武器工厂类 * * @author liangtaiming * @date 2022/10/17 **/ public class WeaponFactory { public static Weapon createWeapon(String type) { if (\u0026#34;small\u0026#34;.equals(type)) { return new Sword(\u0026#34;50\u0026#34;); } else if (\u0026#34;big\u0026#34;.equals(type)) { return new Gun(\u0026#34;1000\u0026#34;); } else { throw new RuntimeException(\u0026#34;无法创建合适的武器类型\u0026#34;); } } } /** * 人 * @author liangtaiming */ @AllArgsConstructor @Data public class Person { public void attack(String enemy){ // 通过调用工厂类 WeaponFactory 创建出不同的武器类,并调用不同武器的方法 Weapon weapon = WeaponFactory.createWeapon(enemy); weapon.attackEnemy(enemy); } } 测试类\n/** * @author liangtaiming * @date 2022/10/09 **/ public class Test { public static void main(String[] args) { String enemyType1 = \u0026#34;big\u0026#34;; String enemyType2 = \u0026#34;small\u0026#34;; Person person = new Person(); person.attack(enemyType1); person.attack(enemyType2); } } 输出结果\nGun can damage enemy big 1000 life Sword can damage enemy small 50 life 可以看出引入简单工厂模式后,会创建一个工厂类,有了这个工厂类之后,具体的武器 Sword 、Gun 类与人 Person 类不再耦合,人需要使用武器的时候不是亲自去创造一个武器,而是通过找工厂,让工厂帮你创造一把武器。\n这样做的好处是:改变武器就不需要修改 Person 类的代码,避免了这两种类之间的硬编码耦合\n不好的地方是:\n额外维护了一个工厂类,增加代码复杂度 虽然解决了调用者与被依赖对象耦合的问题,但工厂类与调用者却耦合在一起,而且当有复杂的多层次等级结构时,所有的业务逻辑都在这个工厂类中实现,当这个工厂类不能工作了,整个系统都会受到影响。 依赖注入 既然这两种解决依赖的方式都不太好,那 Spring 是怎么处理依赖的呢?\nSpring 框架有两个核心功能:\n可以把 Spring 当成一个大工厂(有人称为 Spring 容器),复制创建和管理所有的 Java 对象,这些 Java 对象被称为 Java Bean Spring 这个大工厂使用依赖注入的方式管理着容器中 Bean 之间的依赖关系 使用 Spring 之后,调用者不用主动去创建被依赖对象,只需要被动接受 Spring 容器给你这个对象就行了,从以前的主动获取到被动接受,即将制造武器的控制权从调用方交给了第三方,这种方式被称为“控制反转 IOC(Inverse of Control)”\n但是这个控制反转的说明不够形象,于是有了另一种解读的角度,就是依赖注入 DI (Dependency Injection)\n依赖注入,简单来说就是 Spring 将被依赖对象注入给调用者,调用者不用主动获取被依赖对象,相当于给调用者注入它依赖的对象,这个解释就很简洁明了\n依赖注入让 Spring 容器中 Bean 以配置文件的方式组织在一起,不仅可以为 Bean 注入普通的属性值,还可以注入其他 Bean 的引用,调用者既无须创建被依赖对象,也无须主动定位工厂,与被依赖对象、工厂都进行了耦合,所有的 Bean 之间的依赖关系都交由 Spring 来管理,让调用者可以当一个甩手掌柜。\n那 Spring 以什么样的方式将 Bean 注入到调用者的呢?\n依赖注入的方式 构造函数注入 构造函数注入: 通过调用类的构造函数,将接口实现类通过构造函数的变量进行注入\n下面就开始展示如何使用 Spring 将依赖的对象注入到调用方\n版本依赖使用 maven ,使用 Spring 的版本为 5.3.22\n引入的依赖:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;project xmlns=\u0026#34;http://maven.apache.org/POM/4.0.0\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u0026#34;\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;groupId\u0026gt;com.mingyu\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring_test\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;maven.compiler.source\u0026gt;8\u0026lt;/maven.compiler.source\u0026gt; \u0026lt;maven.compiler.target\u0026gt;8\u0026lt;/maven.compiler.target\u0026gt; \u0026lt;file.encoding\u0026gt;UTF-8\u0026lt;/file.encoding\u0026gt; \u0026lt;spring.version\u0026gt;5.3.22\u0026lt;/spring.version\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;dependencies\u0026gt; \u0026lt;!-- 依赖的 Spring 模块类库 --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-core\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${spring.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-beans\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${spring.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-context\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${spring.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;/dependencies\u0026gt; \u0026lt;/project\u0026gt; 由于引入 Spring 框架,我们需要使用 Spring 特定的管理 Bean 对象的方法,使用配置文件的方式创建和管理 Bean,配置文件的名字一般为 beans.xml,放置在 resources 目录下\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;!-- 配置 person 实例,其实现类是 Person --\u0026gt; \u0026lt;bean id=\u0026#34;person\u0026#34; class=\u0026#34;com.mingyu.javalearn.Person\u0026#34;\u0026gt; \u0026lt;!-- 下面只有一个 constructor-arg 子元素, 驱动 Spring 调用 Person 带一个参数的构造器来创建对象 --\u0026gt; \u0026lt;constructor-arg ref=\u0026#34;sword\u0026#34; type=\u0026#34;com.mingyu.javalearn.Weapon\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;!-- 配置 sword 实例,其实现类是 Sword --\u0026gt; \u0026lt;bean id=\u0026#34;sword\u0026#34; class=\u0026#34;com.mingyu.javalearn.Sword\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;50\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;!-- 配置 gun 实例,其实现类是 Gun --\u0026gt; \u0026lt;bean id=\u0026#34;gun\u0026#34; class=\u0026#34;com.mingyu.javalearn.Gun\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;1000\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;/beans\u0026gt; 人 Person 类跟上面的不一样\n/** * 人 * @author liangtaiming */ @Data public class Person { private Weapon weapon; /** * 构造注入所需的带参数的构造器 * @param weapon 被注入的对象 */ public Person(Weapon weapon) { this.weapon = weapon; } public void attack(String enemy){ // 调用依赖对象 weapon 的 attackEnemy()方法 weapon.attackEnemy(enemy); } } Sword 类、 Gun 类 和 Weapon 类维持不变\n测试类\n/** * @author liangtaiming * @date 2022/10/09 **/ public class Test { public static void main(String[] args) { // 创建Spring容器 try (ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(\u0026#34;beans.xml\u0026#34;)){ // 获取person实例 Person person = ctx.getBean(\u0026#34;person\u0026#34;, Person.class); // 调用attack()方法 person.attack(\u0026#34;Boss\u0026#34;); } } } 输出结果\nSword can damage enemy Boss 50 life 可以观察到我们调用 person 对象的 attack() 方法中实际调用的是依赖对象 weapon 的 attackEnemy()方法,但从输出结果可以看出最终调用的却是 sword 对象的 attackEnemy() 方法,那到底是哪里导致我引用的是 weapon 对象,出现的却是 sword 对象\n从新引入的 beans.xml 文件可以看到有这些配置\n\u0026lt;bean id=\u0026#34;person\u0026#34; class=\u0026#34;com.mingyu.javalearn.Person\u0026#34;\u0026gt; \u0026lt;constructor-arg ref=\u0026#34;sword\u0026#34; type=\u0026#34;com.mingyu.javalearn.Weapon\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;sword\u0026#34; class=\u0026#34;com.mingyu.javalearn.Sword\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;50\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;gun\u0026#34; class=\u0026#34;com.mingyu.javalearn.Gun\u0026#34;\u0026gt; \u0026lt;constructor-arg index=\u0026#34;0\u0026#34; value=\u0026#34;1000\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean.../\u0026gt; 这个元素代表一个 Java Bean 对象 \u0026lt;bean../\u0026gt; 中 id 在整个容器中是这个对象的唯一身份标识,不能与其他对象重复,Spring 通过 id属性来管理 Bean \u0026lt;bean../\u0026gt; 中 class 代表是由哪个类生成的对象实例,使用全类路径进行填写,Spring 会使用 XML 解析器读取并利用反射来创建类实例 在 \u0026lt;bean.../\u0026gt; 中有这么一个 \u0026lt;constructor-arg../\u0026gt; 子元素,每一个 \u0026lt;constructor-arg../\u0026gt; 元素代表一个构造器参数, Spring 就是根据这个子元素调用父元素对象的有参构造函数进行创建对象 如果没有声明 \u0026lt;constructor-arg../\u0026gt; 子元素,就是默认驱动 Spring 调用无参构造器类创建对象 如果有 N 个 \u0026lt;constructor-arg../\u0026gt; 子元素,Spring 就会调用带 N 个参数的构造器去创建对象 Index 属性用于定义构造函数中参数的位置,从下标为 0 开始 value 属性则是入参的值 \u0026lt;construct-rg\u0026gt;中有不少属性是可以进行配置的\n属性 作用 value 传给构造方法参数的值 ref 传给构造方法参数的 Bean ID type 构造方法参数对象的类型 index 构造方法参数对应的位置,从 0 开始计算 name 构造方法参数对应的名称 在\u0026lt;constructor-arg../\u0026gt; 元素中可以看到,它指定了构造器参数的类型为com.mingyu.javalearn.Weapon,通过 type 属性进行指定的,这代表的是 Person 类的对象 person 通过有参构造器注入 Weapon 类的对象,从这里对象间的依赖关系就确定了\nWeapon 是一个接口,接口不能直接使用,那具体使用的是哪个类的对象进行注入呢\n其实 Spring 有 2 种匹配入参的方法:\n根据类型匹配入参—— 通过 ref 和 type 这2个属性匹配 根据索引匹配入参—— 通过 index 和 value 这2个属性匹配 通过 ref 这个属性进行再一步的指定,这里指定的 sword ,然后 Spring 就会通过 sword 找到 id 为 sword 的 Java Bean 对象(id 是唯一的,所以是通过找 id 确定的 Bean)进行对 person 对象的依赖注入\n最后注入到 person 对象中的对象就是com.mingyu.javalearn.Sword 的对象,所以最后输出的结果才是Sword can damage enemy Boss 50 life\n如果我们将\u0026lt;constructor-arg../\u0026gt; 中的 ref 属性改为 gun ,输出的结果就会变为:Gun can damage enemy Boss 1000 life\n那将ref 属性随便改可以吗,改成 test 之类的?\n答案是不行的,因为 Spring 找不到 id 为 test 的 \u0026lt;bean../\u0026gt; 元素,找不到就会报错Cannot resolve bean 'test' 那如果我把 Test 类创建出来,并在 beans.xml 中配置了 test 这个 bean ,再把ref 属性改为 test 能成功吗\n答案也是不行的,虽然会帮你找到 id 为 test 的 \u0026lt;bean../\u0026gt; 元素,但是这个 Test 类并没有对 Weapon 接口进行实现,即与 Weapon 接口没有任何关系,强行写 test 就会报Bean must be of 'com.mingyu.javalearn.Weapon' type 这个错误 到这里我们知道了 Spring 怎么管理对象之间的依赖关系的,也了解了一点 Bean 的定义以及配置,那到底 Spring 是怎么进行依赖注入的呢?\n下面用一段伪代码说明一下 Spring 具体做了一些什么事情\nString id = person // Spring 解析\u0026lt;bean../\u0026gt;元素得到id属性为person String ref = sword // Spring 解析\u0026lt;constructor-arg../\u0026gt;元素得到ref属性为sword // container 代表 Spring 容器 Object paramBean = container.get(ref); // 通过 person 对象的构造函数对 ref 这个 Bean 进行注入 Object obj = new com.mingyu.javalearn.Person(paramBean); // 最后将 person 这个 Bean 以及这个 Bean 所依赖的关系都会存储在 Spring 容器中 container.put(id, obj); 由于使用了有参构造器创建 Person 实例,所以当 Person 实例对象被创建完成之际,该 Bean 的依赖关系已经设置完成\n属性注入 使用构造函数进行注入,虽然依赖关系更加直观(我要依赖哪个对象在创建对象的时候就声明好了),但这种方法也有局限的地方\n如果 A 对象依赖的对象不仅只有 B 对象,还包括其他很多对象,这个时候再采用构造器进行注入就会将对象的构造函数变得很臃肿(构造器 0含有很多入参) 而且 A 对象在某些行为中并不一定都需要依赖所有对象,比如:人在攻击行为中会用到武器这种对象,但在吃饭行为中并不需要武器,需要的却是筷子🥢,所以如果在人构造初始化的时就把武器对象进行注入,就显得不合适,正确的做法应该是哪里需要,注入哪里 这个时候就需要采用属性注入的方法了。属性注入指的是通过 setXxx() 方法注入 Bean 的属性值或依赖对象,属性注入是最常用的注入方式。\n这时需要修改这两个文件 Person.java 和 beans.xml\n/** * 人 * @author liangtaiming */ @Data public class Person { private Weapon weapon; /** * 属性注入的setter方法 * @param weapon 注入的对象 */ public void setWeapon(Weapon weapon) { this.weapon = weapon; } public void attack(String enemy){ // 调用依赖对象 weapon 的 attackEnemy()方法 weapon.attackEnemy(enemy); } } \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;!-- 配置 person 实例,其实现类是 Person --\u0026gt; \u0026lt;bean id=\u0026#34;person\u0026#34; class=\u0026#34;com.mingyu.javalearn.Person\u0026#34;\u0026gt; \u0026lt;!-- 下面只有一个 constructor-arg 子元素, 驱动 Spring 调用 Person 带一个参数的构造器来创建对象 --\u0026gt; \u0026lt;property name=\u0026#34;weapon\u0026#34; ref=\u0026#34;sword\u0026#34;/\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;!-- 配置 sword 实例,其实现类是 Sword --\u0026gt; \u0026lt;bean id=\u0026#34;sword\u0026#34; class=\u0026#34;com.mingyu.javalearn.Sword\u0026#34;/\u0026gt; \u0026lt;!-- 配置 gun 实例,其实现类是 Gun --\u0026gt; \u0026lt;bean id=\u0026#34;gun\u0026#34; class=\u0026#34;com.mingyu.javalearn.Gun\u0026#34;/\u0026gt; \u0026lt;/beans\u0026gt; 仔细观察可以发现 Person 类中将构造函数修改为 setXxx()方法。在属性注入当中,Spring 会先调用 Bean 的默认构造函数实例化 Bean 对象,然后通过反射的方法调用 Setter 方法注入属性值。这里的 Person 类一看并没有默认的构造方法,其实如果我们没有显式定义构造函数 ,JVM会自动生成一个默认构造函数的。\n而 beans.xml 中将 \u0026lt;constructor-arg../\u0026gt; 元素改为 \u0026lt;property../\u0026gt; 元素,而 name 属性指的是 Person 类中需要被注入的属性值\n这次 Spring 就会根据\u0026lt;property../\u0026gt; 元素调用对应的 setter 方法为 Bean 的成员变量注入依赖的类对象实例\n依赖方式的选择 构造函数注入的优点: 构造函数注入可以保证 Bean 一些重要的属性在 Bean 实例化时就准备好 不需要为每个属性提供 Setter 方法,减少类的方法个数 属性注入的优点: 更适用于类属性多的情况 更利于类的继承与扩展,因为子类不需要引用父类复杂的构造方法 避免循环依赖的问题 Spring 并不强制使用哪一种方法,一般建议采用以属性注入为主,构造依赖注入为辅,对于依赖关系没有变化的注入,尽量采用构造器注入,其他的则采用属性注入\n参考资料 \u003c!DOCTYPE HTML\u003e \u003c!DOCTYPE HTML\u003e ","permalink":"http://www.lmingyu.tech/posts/tech/spring-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/","summary":"\u003cblockquote\u003e\n\u003cp\u003eSpring 的学习笔记——依赖注入,本文代码存放在 \u003ca href=\"https://github.com/lmingyul/springlearn\"\u003egithub 仓库\u003c/a\u003e中\u003c/p\u003e\n\u003c/blockquote\u003e","title":"依赖注入"},{"content":" 《我是一只IT小小鸟》阅读笔记\n\u003c!DOCTYPE HTML\u003e 🚀 三句话总结本书 以 5 个人的视角讲述了他们与 IT 行业的之间的故事 文章以总分总的方式叙述了每一个人的故事 每个人或多或少都在时代的浪潮中与 IT 行业结下不解之缘 🎨 简单总结和感受 很多人都是因为一个很特殊的契机进入了计算机这行业,讲述了他们如何进入这个行业,在这个行业遇到了哪些问题并如何进行解决的,并给出了他们在这个行业总结出来的经验,还是很受用的。\n不足地方:五位主人公视角处于偏职业前期,对我这种新人还是不错的,但是对在行业已经很多年的人估计是看一个忆往当年的感觉\n☘️ 为什么我要读这本书 《暗时间》的作者刘未鹏是书里其中一个主人公\n✍️ 最精华的3句书摘 “找书自学是学知识的好方法,同时,把题从头到尾做一遍是很好的自学方法。看书确实是学习一门新知识的好途径,而跟着书上的例子,自己亲身经历一遍,做一次,这也是一种学习的好方法“ “成长路径中有很多的偶然,没人知道在一些关键时候需要什么样的知识储备,所以踏实地多储备一些是好事。我在大四的经历和其他的一些经历都告诉我,以前通过看书、自学,彻底地弄懂的一样知识,会在某个不经意的时候,突然被用到。这时候对当年积累的庆幸和再发现的快乐,就像蓦然回首见到灯火阑珊处的秋水伊人一样,是没法用文字描绘的。” “坚持在读书的时候把代码一行行亲手敲出来并编译通过。这种学习方法很需要毅力,毕竟当你看着书上的代码觉得很容易懂的时候往往懒于动手,但也只有动手才能发现各种问题,比如用C++写class总是忘了最后的分号,缩进不一致导致少写一边大括号,等等。” 📒 书摘 “我们必须在年轻的时候有意地培养自己独立思考的能力,经常想一想,我要的是什么、为什么要做这件事、做这件事有什么好方法、这样做有什么好处和坏处、如何改进等,也要与人交流、沟通,讲出自己的想法,请别人指正。我们需要培养的是自己的思考方式,而不是简单地获取知识” “每看到一个题目,总是会先看答案,让答案来指引我的思路,而不是用自己的脑子想问题。 这种学习方式在短期内的确会取得很大的成果,但却贻害无穷。长此以往,会使人丧失判断力、理解力和交流能力,只留下可怜的记忆力 这样的人难有大成。这样的学习方式会使我们的思考方式沦为简单地重复和机械地回忆,胆子变小,创新力几乎丧失 对于计算机这种快速变化和要求很高的学科来说,显然没有什么价值。” “做自己的主人。不要辜负上帝给我们的聪慧的大脑,用它来独立地想问题,用自己的眼睛来看世界,用自己的心灵来感受,用自己的金口来提问。不要害怕,不要退缩” “加强实践与执行力。不要欠生活,不要欠工作。出来混,总是要还的,你不会的知识,你懒于想通的东西,总是会在一个必要的时候提醒你、惩罚你。将没有搞懂的、希望做到的东西,写在纸上、贴在面前,认真实践,当你做到的越来越多的时候,你就会越来越自信,你的层次会提高的。相信我,人与人之间的差距很大,原因在于自我控制力有差距。” “找工作就如同婚姻,一定要找到适合自己的,千万不能海投,到时候一个你不喜欢的公司给了你Offer而且还逼签,你喜欢的公司又迟迟没有回音,这个时候是签还是不签呢?与其那个时候痛苦,不如从一开始就选择好目标。而且,找工作和实习不同,实习不喜欢可以走人,但是工作是不行的。” “调整心态。没有什么必须要得到的观念,只是顺其自然,这样我非常放松,少了急躁,能够冷静的思考。化被动为主动。主动秀出擅长的东西,整个人自信了许多,自然可以加分。总而言之就是:放轻松,多自信,把面试官当朋友来看待,享受每一次过程,哪怕是被鄙视的过程。” “今天很残酷,明天更残酷。但后天很美好,大部分人将死在明天晚上,看不到后天的太阳。” “找书自学是学知识的好方法,同时,把题从头到尾做一遍是很好的自学方法。看书确实是学习一门新知识的好途径,而跟着书上的例子,自己亲身经历一遍,做一次,这也是一种学习的好方法“ “慢即是快,笨笨地做一遍题是学习的捷径” “一字不漏敲入一本书的程序成了我推荐别人学习语言的最好办法” “成长路径中有很多的偶然,没人知道在一些关键时候需要什么样的知识储备,所以踏实地多储备一些是好事。我在大四的经历和其他的一些经历都告诉我,以前通过看书、自学,彻底地弄懂的一样知识,会在某个不经意的时候,突然被用到。这时候对当年积累的庆幸和再发现的快乐,就像蓦然回首见到灯火阑珊处的秋水伊人一样,是没法用文字描绘的。” “我有个切身的教训,就是要踏实地积累。我读书涉猎很广,很多东西我都能大概知道怎么回事,但是就是学得不深刻,所以即使当时花了很多力气,需要的时候还要花大功夫重头捡起。所以我的教训就是,与其两个半瓶水,不如一个满瓶水。这个道理用在读书上就是说,一本好书读两次要胜过两本好书各读一次。要是大学能重新来过一次,我会少读一些书,多读透一些书,这可能是我觉得当年猛读书的唯一遗憾。” “真正基础雄厚的人应该是可以将常用的基础性知识熟记成诵的人,提到一个基本的名词或话题能够将其来龙去脉解释清楚的人。” “事实证明,任何牛逼的软件都不是一下子就那么牛逼的,必须要循序渐进地展开。” “坚持在读书的时候把代码一行行亲手敲出来并编译通过。这种学习方法很需要毅力,毕竟当你看着书上的代码觉得很容易懂的时候往往懒于动手,但也只有动手才能发现各种问题,比如用C++写class总是忘了最后的分号,缩进不一致导致少写一边大括号,等等。” ","permalink":"http://www.lmingyu.tech/posts/input/%E6%88%91%E6%98%AF%E4%B8%80%E5%8F%AAit%E5%B0%8F%E5%B0%8F%E9%B8%9F%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003e《我是一只IT小小鸟》阅读笔记\u003c/p\u003e\n\u003c/blockquote\u003e","title":"《我是一只IT小小鸟》读书笔记"},{"content":" 回顾过去一个月,过去这个月还是过的很快,工作上很忙,感觉什么事情都要我来处理,中秋也加班了1 天,生活上也没有发生比较明显的变化,就是 24 小时的核酸都已经持续 1 个多月了,不知道什么时候能结束,还是希望生活和工作能越来越好吧\n输入 📚 阅读 总计阅读 4 本书,1 本读完,3 本在读\n《小狗钱钱》\n《暗时间》—— Reading\u0026hellip;\n《我们为什么要睡觉》—— Reading\u0026hellip;\n《你是你吃出来的》—— Reading\u0026hellip;\n🎬 电影、电视剧 总计电影 1 部,电视剧无\n电影: \u003c!DOCTYPE HTML\u003e 最近疫情又开始反复,电影院和其他的娱乐场所很多都没开,周末趁这部电影还在上映就赶紧买票去看。作为一个土生土长的广东人第一次看港式喜剧片电影,觉得还是不错的,推荐粤语版的。 电视剧——无 🎤 播客 PodCast 最近喜欢上听播客这一种输入方式,在上班路上听,吃完饭之后听,下班路上听,排队做核酸听,洗澡的时候听\u0026hellip; 总之就是碎片化的时间都用来听播客,开始觉得这种输入方式对于改变自己碎片化时间的消耗还不错。听播客的平台是小宇宙\n总计听完 32 个播客,这个月收听博客时长为 34 小时34 分钟\n觉得还不错的博客:\n输出 博客文章 总计 6 篇文章\nJava 学习笔记 2 篇 MySQL 学习笔记 3 篇 Redis 学习笔记 1 篇 项目 暂无进展\n","permalink":"http://www.lmingyu.tech/posts/life/2022-9%E6%9C%88%E5%9B%9E%E9%A1%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e回顾过去一个月,过去这个月还是过的很快,工作上很忙,感觉什么事情都要我来处理,中秋也加班了1 天,生活上也没有发生比较明显的变化,就是 24 小时的核酸都已经持续 1 个多月了,不知道什么时候能结束,还是希望生活和工作能越来越好吧\u003c/p\u003e\n\u003c/blockquote\u003e","title":"2022年9月回顾"},{"content":" 《小狗钱钱》阅读笔记\n\u003c!DOCTYPE HTML\u003e 🚀 三句话总结本书 以小狗的姿态教会我们理财的知识 以讲故事的方式,讲述通过合理的理财方法可以改善生活,甚至改变人生 理财可以从娃娃抓起 🎨 简单总结和感受 作为理财的科普入门书,通过小孩和小狗的视角讲述一个通常意义上理财的知识,还是不错的。\n不足的地方:仅仅是讲述怎么理财,但没有更深入地解释为什么这样做是对的,但作为入门还是可以的,有些方法还是可以付之于实践。而且有些理念我是不太认同的,比如说不使用信用卡,我信用卡只用于大件物品,用免息分期摊平资金使用的时间成本,适当的小杠杆对我个人还是有用处的。\n☘️ 为什么我要读这本书 作为个人入门理财的第一本书\n✍️ 最精华的3句书摘 当你决定要做一件事情时,必须在 72 小时之内开始行动,否则极有可能不会再做 幸运是充分准备加努力工作的结果 利用碎片化的时间,不要等待,每天 10 分钟,可以给你带来真正的改变 📒 书摘 金钱本身并不会使人幸福,它是中性的,只有当它属于一个人时才会具备好或坏的意义 如何实现梦想: 了解自己,知道自己擅长的地方 养成习惯,每天在固定时间完成计划 当一切进展顺利的时也要坚持计划 分期付款尽量让每次还款对你的影响达到最低 量入为出 ","permalink":"http://www.lmingyu.tech/posts/input/%E5%B0%8F%E7%8B%97%E9%92%B1%E9%92%B1%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003e《小狗钱钱》阅读笔记\u003c/p\u003e\n\u003c/blockquote\u003e","title":"《小狗钱钱》读书笔记"},{"content":" 记录一下 redis 命令行中常用的一些命令\n键 查看所有的键 keys * 示例:\n127.0.0.1:6379\u0026gt; set hello world OK 127.0.0.1:6379\u0026gt; set java jedis OK 127.0.0.1:6379\u0026gt; set python redis-py OK 127.0.0.1:6379\u0026gt; keys * 1) \u0026#34;hello\u0026#34; 2) \u0026#34;python\u0026#34; 3) \u0026#34;java\u0026#34; 查看键总数 # 返回当前数据库中键的总数 dbsize 示例:\n127.0.0.1:6379\u0026gt; dbsize (integer) 3 key * 与 dbsize 区别: dbsize 命令在计算键总数的时候不会遍历数据库中所有的键,而是直接返回一个叫键总数变量的值,这个键总数变量存储的就是当前数据库中键的数量,所以 dbsize 命令执行的时间复杂度为 O(1) keys * 命令会遍历数据库中所有的键,这个命令执行的时间复杂度为 O(n),所以在线上环境保存着大量键的情况下,这个命令可能会导致 Redis 阻塞的,不建议在生产环境上使用 检测键是否存在 # 如果键存在则返回 1,不存在则返回 0 exists key 示例:\n127.0.0.1:6379\u0026gt; exists java (integer) 1 127.0.0.1:6379\u0026gt; exists not_exist_key (integer) 0 删除键 # 返回的是成功删除键的个数,如果删除的键不存在,则会返回 0 del key 示例:\n127.0.0.1:6379\u0026gt; del java (integer) 1 127.0.0.1:6379\u0026gt; exists java (integer) 0 127.0.0.1:6379\u0026gt; del not_exist_key (integer) 0 设置键过期 # 当超过超时时间后,会自动删除键,单位为秒 expire key seconds 示例:\nttl 命令会返回键的剩余过期时间,它有 3 种返回值: 大于等于 0 的整数:键剩余的过期时间 -1 :键没设置过期时间 -2 :键不存在 127.0.0.1:6379\u0026gt; expire hello 10 (integer) 1 # 剩余 4 秒 127.0.0.1:6379\u0026gt; ttl hello (integer) 4 127.0.0.1:6379\u0026gt; ttl hello (integer) 1 127.0.0.1:6379\u0026gt; ttl hello (integer) -2 127.0.0.1:6379\u0026gt; get hello (nil) 查看键的类型 # 返回键的类型,redis 数据结构类型一共包含 5 种 type key 示例:\n127.0.0.1:6379\u0026gt; keys * 1) \u0026#34;python\u0026#34; 127.0.0.1:6379\u0026gt; type python string 单个键 键重命名 rename key newKey 示例:\n127.0.0.1:6379\u0026gt; get python \u0026#34;jedis\u0026#34; 127.0.0.1:6379\u0026gt; rename python java OK 127.0.0.1:6379\u0026gt; get python (nil) 127.0.0.1:6379\u0026gt; get java \u0026#34;jedis\u0026#34; 字符串 字符串类型是 Redis 中最基础的数据结构\n键为字符串,值可以是字符串、数字、二进制数据(图片、音频),二进制数据的最大值不能超过 512 MB\n常用命令 设置值 设置单个值 以下操作设置键值对,返回结果 OK 代表设置成功\nset key value 示例:\n127.0.0.1:6379\u0026gt; set hello world OK 键 key 不存在才能设置成功,常用于添加新的键值对,常见场景是实现分布式锁,这个命令等同于 set key value nx\nsetnx key value 键 key 存在才能设置成功,常用于更新\nset key value xx 示例:\n# hello 键不存在 127.0.0.1:6379\u0026gt; exists hello (integer) 0 127.0.0.1:6379\u0026gt; set hello world OK # 因为键 hello 已存在,所以 setnx 失败,结果返回为 0 127.0.0.1:6379\u0026gt; setnx hello world (integer) 0 # 因为键 hello 已存在,所以 set xx 成功,结果返回为 OK 127.0.0.1:6379\u0026gt; set hello jedis xx OK 批量设置值 mset key1 value1 key2 value2 ... 示例:\n127.0.0.1:6379\u0026gt; mset a 1 b 2 c 3 d 4 OK 获取值 获取单个值 # 如果 key 不存在,则返回 nil (空) get key 示例:\n127.0.0.1:6379\u0026gt; get hello \u0026#34;jedis\u0026#34; 批量获取值 mget key1 key2 ... 示例:\n127.0.0.1:6379\u0026gt; mget a b c d 1) \u0026#34;1\u0026#34; 2) \u0026#34;2\u0026#34; 3) \u0026#34;3\u0026#34; 4) \u0026#34;4\u0026#34; 计数 用于对值做自增操作\n值如果不是整数,返回错误 值是整数,返回自增后的结果 键不存在,按照值为 0 自增,返回结果 1 incr key 示例:\n27.0.0.1:6379\u0026gt; exists key (integer) 0 # 键不存在,按照值为 0 自增,返回结果 1 127.0.0.1:6379\u0026gt; incr key (integer) 1 127.0.0.1:6379\u0026gt; incr key (integer) 2 # 值不是整数,返回错误 127.0.0.1:6379\u0026gt; set hello world OK 127.0.0.1:6379\u0026gt; incr hello (error) ERR value is not an integer or out of range 哈希 常用命令 设置值 # 键: key 、值: field value hset key field value 示例 :\n127.0.0.1:6379\u0026gt; hset user name mingyu (integer) 1 获取值 # 如果键或 field 不存在,则会返回 nil hget key field 示例:\n127.0.0.1:6379\u0026gt; hget user name \u0026#34;mingyu\u0026#34; # field 不存在,返回 nil 127.0.0.1:6379\u0026gt; hget user age (nil) 删除 field # 结果返回成功删除 field 的个数 hdel key field 示例:\n127.0.0.1:6379\u0026gt; hdel user name (integer) 1 127.0.0.1:6379\u0026gt; hdel user age (integer) 0 计算 field 个数 hlen key 示例:\n127.0.0.1:6379\u0026gt; hset user name mingyu (integer) 1 127.0.0.1:6379\u0026gt; hset user age 18 (integer) 1 # 键 user 含有 2 个 field: name、age 127.0.0.1:6379\u0026gt; hlen user (integer) 2 判断 field 是否存在 # 存在返回 1, 不存在返回 0 hexists key field 示例:\n127.0.0.1:6379\u0026gt; hexists user name (integer) 1 127.0.0.1:6379\u0026gt; hexists user city (integer) 0 获取所有 field hkeys key 示例:\n127.0.0.1:6379\u0026gt; hkeys user 1) \u0026#34;name\u0026#34; 2) \u0026#34;age\u0026#34; 获取所有 value hvals key 示例:\n127.0.0.1:6379\u0026gt; hvals user 1) \u0026#34;mingyu\u0026#34; 2) \u0026#34;18\u0026#34; 列表 列表是用于存储多个有序的字符串\n在 Redis 中,不仅可以对列表两端进行插入(push)和弹出(pop)的操作,还可以进行获取指定范围的元素列表、获取指定索引下标的元素等操作。列表是一种比较灵活的数据结构,可以充当栈和队列。\n列表的特点: 元素是有序的(可以获取某个范围的元素列表) 列表中的元素可以是重复的 常用命令 添加操作 # 从右边插入元素 rpush key value [value ...] # 从左边插入元素 lpush key value [value ...] 示例:\n127.0.0.1:6379\u0026gt; lpush listkey b a c (integer) 3 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;c\u0026#34; 2) \u0026#34;a\u0026#34; 3) \u0026#34;b\u0026#34; 查找 获取指定范围内的元素列表 列表下标从左到右是 0 ~ n - 1,从右到左是 -1 ~ - n\nlrange key start end # 可以获取从左到右所有列表元素 lrange listkey 0 -1 获取指定下标的元素 lindex key index 示例:\n127.0.0.1:6379\u0026gt; lindex listkey -1 \u0026#34;b\u0026#34; 获取列表长度 llen key 示例:\n127.0.0.1:6379\u0026gt; llen listkey (integer) 3 删除 从列表弹出元素 # 从列表左侧弹出元素 lpop key # 从列表右侧弹出元素 rpop key 示例:\n127.0.0.1:6379\u0026gt; lpop listkey \u0026#34;c\u0026#34; 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;a\u0026#34; 2) \u0026#34;b\u0026#34; 删除指定的元素 这个命令会有 3 种情况:\ncount \u0026gt; 0 从左到右删除最多 count 个元素 count \u0026lt; 0 从右到左删除最多 count 绝对值个元素 count = 0 删除所有元素 lrem key count value 示例:\n127.0.0.1:6379\u0026gt; lpush listkey a a a a a java b a (integer) 8 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;a\u0026#34; 2) \u0026#34;b\u0026#34; 3) \u0026#34;java\u0026#34; 4) \u0026#34;a\u0026#34; 5) \u0026#34;a\u0026#34; 6) \u0026#34;a\u0026#34; 7) \u0026#34;a\u0026#34; 8) \u0026#34;a\u0026#34; 127.0.0.1:6379\u0026gt; lrem listkey 4 a (integer) 4 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;b\u0026#34; 2) \u0026#34;java\u0026#34; 3) \u0026#34;a\u0026#34; 4) \u0026#34;a\u0026#34; 127.0.0.1:6379\u0026gt; 按照索引范围修剪列表 ltrim key start end 示例:\n# 会保留列表 listkey 第 2 ~ 4 个元素 127.0.0.1:6379\u0026gt; ltrim listkey 1 3 OK 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;java\u0026#34; 2) \u0026#34;a\u0026#34; 3) \u0026#34;a\u0026#34; 修改 # 修改指定索引下标的元素 lset key index newValue 示例:\n127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;java\u0026#34; 2) \u0026#34;a\u0026#34; 3) \u0026#34;a\u0026#34; 127.0.0.1:6379\u0026gt; lset listkey 0 a OK 127.0.0.1:6379\u0026gt; lrange listkey 0 -1 1) \u0026#34;a\u0026#34; 2) \u0026#34;a\u0026#34; 3) \u0026#34;a\u0026#34; 集合 集合与列表类型类似,不允许有重复元素,且集合中元素是无序的\n集合内常用命令 添加元素 sadd key element [element...] 示例:\n127.0.0.1:6379\u0026gt; sadd myset a b c (integer) 3 # 集合中已经有a b, 所以此次添加成功元素为 0 个 127.0.0.1:6379\u0026gt; sadd myset a b (integer) 0 删除元素 srem key element [element...] 示例:\n127.0.0.1:6379\u0026gt; srem myset a b (integer) 2 # 集合中并没有 hello 元素,所以删除元素个数为 0 127.0.0.1:6379\u0026gt; srem myset hello (integer) 0 计算元素个数 scard key 示例:\n127.0.0.1:6379\u0026gt; scard myset (integer) 1 判断元素是否在集合中 sismember key element 示例:\n127.0.0.1:6379\u0026gt; sismember myset c (integer) 1 127.0.0.1:6379\u0026gt; sismember myset hello (integer) 0 从集合中随机弹出元素 # 弹出元素会将元素从集合中删除 spop key 示例:\n127.0.0.1:6379\u0026gt; spop myset \u0026#34;c\u0026#34; 127.0.0.1:6379\u0026gt; exists myset (integer) 0 获取所有元素 # 返回结果是无序的 smembers key 示例:\n127.0.0.1:6379\u0026gt; smembers myset (empty array) 127.0.0.1:6379\u0026gt; sadd myset a b c (integer) 3 127.0.0.1:6379\u0026gt; smembers myset 1) \u0026#34;c\u0026#34; 2) \u0026#34;a\u0026#34; 3) \u0026#34;b\u0026#34; 有序集合 与集合类似,不同的是集合中的元素是有序的\n集合内常用命令 添加成员 zadd key score member [score member ...] 示例:\n# 向有序集合 user:ranking 添加用户 127.0.0.1:6379\u0026gt; zadd user:ranking 251 mingyu 100 jack (integer) 2 计算成员个数 zcard key 示例:\n127.0.0.1:6379\u0026gt; zcard user:ranking (integer) 2 计算某个成员的分数 zscore key member 示例:\n127.0.0.1:6379\u0026gt; zscore user:ranking mingyu \u0026#34;251\u0026#34; 127.0.0.1:6379\u0026gt; zscore user:ranking jack \u0026#34;100\u0026#34; 127.0.0.1:6379\u0026gt; zscore user:ranking tom (nil) 计算成员排名 排名从 0 开始\n# 从低到高排名 zrank key member # 从高到低排名 zrevrank key member 示例:\n127.0.0.1:6379\u0026gt; zrank user:ranking mingyu (integer) 1 127.0.0.1:6379\u0026gt; zrevrank user:ranking mingyu (integer) 0 删除成员 zrem key member 示例:\n127.0.0.1:6379\u0026gt; zrem user:ranking jack (integer) 1 127.0.0.1:6379\u0026gt; zcard user:ranking (integer) 1 增加成员分数 zincrby key increment member 示例:\n127.0.0.1:6379\u0026gt; zincrby user:ranking 9 mingyu \u0026#34;260\u0026#34; 参考资料 \u003c!DOCTYPE HTML\u003e ","permalink":"http://www.lmingyu.tech/posts/tech/redis-learn-redis%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e记录一下 redis 命令行中常用的一些命令\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Redis 基本命令"},{"content":" 揭开 redis 的神秘面纱\nRedis入门 什么是 Redis Redis 官网的描述:\nRedis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine.\nRedis是一个开源(BSD许可)的内存数据结构存储,可被用作数据库、缓存、消息代理和流媒体引擎。\n一句话总结:Redis 是一个基于键值对且使用内存存储的非关系型数据库。\nRedis 的特性 读写速度快 因为 Redis 是基于内存存储的数据库,数据都存放在内存当中,CPU 从内存中读取数据是很快的 基于 C 语言实现的,语言的编译及运行都会影响程序的运行,而使用 C 语言执行程序速度会相对其他语言来说更快 使用单线程架构,预防了多线程可能产生的竞争问题 基于键值对的数据结构服务器 Redis 提供了多种数据结构来存储键值对,便于在多种应用场景进行高效开发 丰富的功能 提供键过期的功能,可用于实现缓存 提供发布订阅功能,可用于实现消息系统 支持 Lua 脚本功能 提供简单的事务功能 提供了流水线的功能,使客户端可以将一批命令一次性传到 Redis,减少网络开销 客户端支持的语言众多:包括 Java、PHP、Python、C、C++等 支持数据持久化存储 支持主从复制 高可用和分布式 安装启动使用 Redis 以下介绍的是 Mac 电脑的安装过程\nMac 电脑的配置\nM1 Max 运存:64 G 系统:masOS Monterey 12.4 官网地址\n安装 Homebrew 检测一下 Mac电脑 Homebrew 是否已经安装\n➜ ~ brew --version Homebrew 3.5.6 Homebrew/homebrew-core (git revision 4fc0fed5cda; last commit 2022-07-31) Homebrew/homebrew-cask (git revision ff26ceb965; last commit 2022-07-31) 如果命令失败,可以查看官网进行 Homebrew 的安装\n安装 redis brew install redis 启动 redis 使用默认配置启动 ➜ ~ redis-server 21748:C 25 Sep 2022 22:35:20.650 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 21748:C 25 Sep 2022 22:35:20.650 # Redis version=7.0.3, bits=64, commit=00000000, modified=0, pid=21748, just started 21748:C 25 Sep 2022 22:35:20.650 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 21748:M 25 Sep 2022 22:35:20.651 * Increased maximum number of open files to 10032 (it was originally set to 256). 21748:M 25 Sep 2022 22:35:20.651 * monotonic clock: POSIX clock_gettime _._ _.-``__ \u0026#39;\u0026#39;-._ _.-`` `. `_. \u0026#39;\u0026#39;-._ Redis 7.0.3 (00000000/0) 64 bit .-`` .-```. ```\\/ _.,_ \u0026#39;\u0026#39;-._ ( \u0026#39; , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|\u0026#39;` _.-\u0026#39;| Port: 6379 | `-._ `._ / _.-\u0026#39; | PID: 21748 `-._ `-._ `-./ _.-\u0026#39; _.-\u0026#39; |`-._`-._ `-.__.-\u0026#39; _.-\u0026#39;_.-\u0026#39;| | `-._`-._ _.-\u0026#39;_.-\u0026#39; | https://redis.io `-._ `-._`-.__.-\u0026#39;_.-\u0026#39; _.-\u0026#39; |`-._`-._ `-.__.-\u0026#39; _.-\u0026#39;_.-\u0026#39;| | `-._`-._ _.-\u0026#39;_.-\u0026#39; | `-._ `-._`-.__.-\u0026#39;_.-\u0026#39; _.-\u0026#39; `-._ `-.__.-\u0026#39; _.-\u0026#39; `-._ _.-\u0026#39; `-.__.-\u0026#39; 21748:M 25 Sep 2022 22:35:20.651 # WARNING: The TCP backlog setting of 511 cannot be enforced because kern.ipc.somaxconn is set to the lower value of 128. 21748:M 25 Sep 2022 22:35:20.651 # Server initialized 21748:M 25 Sep 2022 22:35:20.652 * Loading RDB produced by version 7.0.3 21748:M 25 Sep 2022 22:35:20.652 * RDB age 5284694 seconds 21748:M 25 Sep 2022 22:35:20.652 * RDB memory usage when created 1.05 Mb 21748:M 25 Sep 2022 22:35:20.652 * Done loading RDB, keys loaded: 0, keys expired: 0. 21748:M 25 Sep 2022 22:35:20.652 * DB loaded from disk: 0.000 seconds 21748:M 25 Sep 2022 22:35:20.652 * Ready to accept connections 使用的 redis 的版本是 7.0.3 redis 的端口是 6379 如果要停止 redis,按 Ctrl + c 即可\n配置文件启动 redis 目录下都会有一个 redis.conf 文件,里面就是 redis 的默认配置\n生产环境一般通过配置文件启动的方式启动 redis\nredis-server /opt/redis/redis.conf 设置 redis 电脑启动时启动 # 会以后台的方式启动 redis ➜ ~ brew services start redis ==\u0026gt; Successfully started `redis` (label: homebrew.mxcl.redis) # 查看运行中 redis 的状态 ➜ ~ brew services info redis redis (homebrew.mxcl.redis) Running: ✔ Loaded: ✔ Schedulable: ✘ User: mingyu PID: 22716 # 停止 redis 服务 ➜ ~ brew services stop redis Stopping `redis`... (might take a while) ==\u0026gt; Successfully stopped `redis` (label: homebrew.mxcl.redis) 连接 redis 一旦上面的步骤都成功,接下来就可以连接 redis 进行操作\n# 以 redis 客户端方式连接,使用了默认的 ip: 127.0.0.1, port: 6379 ➜ ~ redis-cli 127.0.0.1:6379\u0026gt; # 测试demo 127.0.0.1:6379\u0026gt; lpush demos redis-macOS-demo (integer) 1 127.0.0.1:6379\u0026gt; rpop demos \u0026#34;redis-macOS-demo\u0026#34; 127.0.0.1:6379\u0026gt; 停止 Redis 服务 # 停止 redis 服务 ➜ ~ redis-cli shutdown # 使用 redis-cli 再次连接,就会显示连接不上,此时需要 redis-server 重新启动才可以继续使用 ➜ ~ redis-cli Could not connect to Redis at 127.0.0.1:6379: Connection refused not connected\u0026gt; 关闭 Redis 前,生成持久化文件 redis-cli shutdown save 参考资料 Redis 官网 Redis 开发与运维 ","permalink":"http://www.lmingyu.tech/posts/tech/redis-learn-redis%E5%85%A5%E9%97%A8/","summary":"\u003cblockquote\u003e\n\u003cp\u003e揭开 redis 的神秘面纱\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Redis 入门"},{"content":" 本文记录的是 MySQL DDL 常用的一些命令。DDL (Data Definition Language 数据定义语言)用于操作对象及对象本身,这种对象包括数据库,表对象,及视图对象\nMySQL 不区分大小写,以下统一使用小写来记录。\n数据库 数据库的基本操作 创建数据库 create databse 数据库名; IF NOT EXISTS 如果数据库已经存在就无法再次创建,可以使用以下命令防止创建数据库时发生错误\n# 如果数据库不存在就创建,如果存在则不会进行建表 create databse if not exists 数据库名; 注意:数据库名在 Linux 系统中是区分大小写的\n示例:\ncreate databse if not exists mingyu; 查询已经存在的数据库 show databases; 指定使用的数据库 指定数据库 use 数据库名; 示例:\nuse mingyu; 显示当前使用的数据库 select database(); 删除数据库 drop database if exists 数据库名; 表 表的基本操作 创建表 create table tb1 (empid varchar(10), name varchar(10), age int); 和创建数据库一样,如果遇到同名的表,就会创建失败,也可以使用以下的命令\ncreate table if not exists tb1 (empid varchar(10), name varchar(10), age int); 指定字符编码创建表 当表中含有中文等字符时会容易出现字符乱码的情况,这个时候可以通过创表时指定表的字符编码\ncreate table if not exists tb1 (empid varchar(10), name varchar(10), age int) charset=utf8; 给建表语句添加注释 注释主要用于解释该表的用途\ncreate table 表名 (...) comment \u0026#39;注释信息\u0026#39;; 显示所有的表 # 显示当前选择的数据库下的表 show tables; 显示建表语句 # \\G 以垂直的形式将结果中的每一列都展示在单独的一行中 show create table 表名\\G 确认表的的列结构 desc 表名; 示例:\nmysql\u0026gt; desc tb1; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(10) | YES | | NULL | | | age | int | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql\u0026gt; Field 表示列名 Type 表示数据类型 NULL 表示允许不输入任何值 Key 存储关于键的信息,例如会存 PRI ,这个是主键的缩写 Default 表示如果什么值都不输入就使用这个值 Extra 会显示一些额外的信息,比如说这个列的一些属性 删除表 drop table if exists 表名; 修改表 修改表名 alter table 旧表名 rename to 新表名; 列 修改列 修改列的数据类型 注意: 修改后的数据类型要兼容表中现有的数据,如某一列的数据类型为 varchar(100),修改为 varchar(50),如果表中含有大于 50 字符的数据,修改就会报错\nalter table 表名 modify 列名 新数据类型; 示例:\n+-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(10) | YES | | NULL | | | age | int | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ mysql\u0026gt; alter table tb1 modify name varchar(100); Query OK, 1 row affected (0.03 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql\u0026gt; desc tb1; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) 同时修改列名和数据类型 alter table 表名 change 修改前列名 修改后列名 数据类型; 示例:\nmysql\u0026gt; desc tb1; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | | birth | datetime | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql\u0026gt; alter table tb1 change birth birthday date; Query OK, 1 row affected (0.02 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql\u0026gt; desc tb1; +----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | | birthday | date | YES | | NULL | | +----------+--------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) 添加列 alter table 表名 add column 列名 数据类型; 示例:\nmysql\u0026gt; desc tb1; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql\u0026gt; alter table tb1 add column birth datetime; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql\u0026gt; desc tb1; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | | birth | datetime | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) 删除列 alter table 表名 drop column 列名; 示例:\nmysql\u0026gt; desc tb1; +----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | | birthday | date | YES | | NULL | | +----------+--------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql\u0026gt; alter table tb1 drop column birthday; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql\u0026gt; desc tb1; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | empid | varchar(10) | YES | | NULL | | | name | varchar(100) | YES | | NULL | | | age | int | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) 列的属性 默认值 create table 表名 (列名 类型 default 默认值); 如果不给列设置默认值,相当于给列指定了默认值 NULL\nNOT NULL create table 表名 (列名 类型 not null); 主键 概念: 候选键:我们可以通过某些列或者某些列的组合来锁定表中的某一条数据,这些列或列的组合称为候选键 主键:一个表有多个候选键,可以选择一个候选键作为表的主键 一个表最多只能有一个主键 主键的值不能重复,如果待插入的记录的主键值已经存在则会发生错误 可以通过主键找到唯一的一条记录 创建表的时候可以指定主键:\n# 1.主键为单个列 create table 表名 (列名 类型 primary key); # 2.多个列组合作为主键 create table 表名 (列名 类型, primary key (列名1, 列名2)); UNIQUE 约束 除了主键之外,如果我们也想某些列或某些列的组合存储的值是唯一的,就可以给这个列添加 UNIQUE 约束\n主键与 unique 的区别:\n一张表只能定义一个主键,但可以有多个 unique 约束 主键列不允许存放 NULL ,但声明了 unique 约束列可以存放 NULL ,因为 NULL 并不代表某个具体的值 如果没有定义主键,那么 MySQL 会将第一个声明 NOT NULL 且具有 unique 约束的列或列组合自动定义为主键 # 1.单个列添加约束 create table 表名 (列名 类型 unique); # 2.多个列添加约束 create table 表名 (列名 类型, unique key (列名1, 列名2)); 外键 如果 A 表的某个列或某些列依赖于 B 表某个列或某些列,这个时候就可以用外键将这些列关联起来,A 表被称为子表,B 表被称为父表。当使用了外键关联时,我们往 A 表插入数据的时候,MySQL 会帮我们检测 B 表是否能否可以找到\n语法:\nconstraint foreign key(子表列名) references 父表名(父表需要关联的列名) auto_increment auto_increment 为自动增长的意思,类型属性可以为整数或浮点类型,当我们插入一条新数据的时候,可以不指定该列的值,MySQL 会自动帮该列生成一个自动增长的值,这个值是在当前列中最大值的下一个值,例如当前列中含有 1, 2, 3, 8,那下一个自增长值为 9,而不是 4 。\n列名 类型 auto_increment 注意:\n如果声明了 auto_increment 的那一列在插入数据的显式的赋值了,那就以指定的值为准 一个表中最多有一个具有 auto_increment 属性的列 具有 auto_increment 属性的列必须建立索引。 具有 auto_increment 属性的列不能通过指定 default 属性来指定默认值 参考资料 MySQL基础教程 MySQL 8.0参考手册 MySQL是怎样使用的 快速入门MySQL ","permalink":"http://www.lmingyu.tech/posts/tech/mysql-learn-2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文记录的是 MySQL DDL 常用的一些命令。DDL (Data Definition Language 数据定义语言)用于操作对象及对象本身,这种对象包括数据库,表对象,及视图对象\u003c/p\u003e\n\u003c/blockquote\u003e","title":"MySQL 基础-DDL 命令"},{"content":" 本文记录的是 MySQL DML、DQL 常用的一些命令。\nDML(Data Manipulation Language 数据操控语言) 用于操作数据库表对象中包含的数据\nDQL (Data Query Language 数据查询语言 )用于查询数据\nDML 插入数据 插入一条数据 # 在 values 后面的()中,插入的数据需要按照列的顺序用逗号进行分割数据 insert into 表名 values(数据 1, 数据 2 ...); 示例:\nmysql\u0026gt; insert into tb1 values(\u0026#39;01\u0026#39;, \u0026#39;mingyu\u0026#39;, 18); Query OK, 1 row affected (0.01 sec) 注意点:\n字符串数据需要用 \u0026quot;\u0026quot;双引号 或 ''单引号括起来 日期与时间数据也需要使用\u0026quot;\u0026quot;双引号 或 ''单引号括起来,日期必须以 YYYY-MM-DD 的格式输入,时间必须以 HH:MM:SS 的格式输入 指定列名插入数据 如果不填列名就是默认给所有列插入数据\n可以忽略列的顺序进行插入记录\ninsert into 表名 (列名 1, 列名 2 ...) values(数据 1, 数据 2 ...); 插入多条数据 insert into 表名 (列名 1, 列名 2 ...) values(数据 1, 数据 2 ...),(数据 1, 数据 2 ...); 将某个查询的结果插入表中 insert into 表1 (列名 1, 列名 2) select 列 1, 列 2 from 表 2; 从表 2 插入的列需要与被插入表 1 的列一一对应\n插入的其他操作 插入的数据如果在表中存在就不进行任何操作,如果不存在则插入\ninsert ignore into 表名 values(数据 1, 数据 2 ...); 插入的数据如果在表中存在就对某些列进行更新,如果不存在则插入\ninsert into 表名 values(数据 1, 数据 2 ...) on duplicate key update 列名1 = \u0026#39;\u0026#39;, 列名2 = \u0026#39;\u0026#39;; 删除数据 # 不加 where 表达式代表删除表中所有数据 delete from 表名 [where 表达式]; 更新数据 update 表名 set 列1 = 值1, 列2 = 值2 [where 表达式]; DQL 简单查询 查询多个列 查询表的数据,select 语句是使用频率最高的命令,查询所有列的也是最常用的:select * from 表名;\nselect 列名 1, 列名 2... from 表名; 示例:\nmysql\u0026gt; select empid, name from tb1; +-------+--------+ | empid | name | +-------+--------+ | 01 | mingyu | +-------+--------+ 1 row in set (0.00 sec) 列的别名 可以给结果集中的列重新定义一个别名\nselect 列名 [as] 列的别名 from 表名; 这个as 可有可无,如果没有 as ,列和列的别名之间使用空格进行分割,如:\nSELECT number as 学号 FROM student_info; SELECT number 学号 FROM student_info; 查询结果去重 去除单列 select distinct 列名 from 表名; 去除多列 多列重复即查询的每一条结果中每一列都相同\nselect distinct 列名1, 列名2 ... from 表名; 限制结果数量 select 列名 from 表名 limit 偏移量 限制条数; 这个语句的意思是:从偏移量的地方开始,查询出限制条数的结果 limit 后面如果只有一个数,说明偏移量为 0,即默认从第 1 条数据开始查询 如果偏移量大于或等于所有数据的行数,查询出来的结果就是空集 对查询结果排序 对查询结果按照某一个列进行排序\nselect 列名 from 表名 order by 列名 [ASC|DESC]; ASC 代表查询结果按照由小到大排序,即升序 DESC 代表查询结果按照由大到小排序,即降序 两种排序方式只能选其中一个 如果两种都不填,默认使用 ASC 进行升序排序 不仅可以按照数字类型的列进行排序,也可以通过字符类型的列进行排序,排序的规则是按照每个字符的大小进行比较得出的 当 order by 子句和 limit 子句一起使用的时候,order by 子句必须放置在 limit 子句前面 简单查询中各子句的顺序 select [distinct] 查询列 [from 表名] [where 过滤条件] [group by 需要分组的列] [having 分组过滤条件] [order by 排序的列名] [limit 偏移量, 限制条数] 中括号 [] 中的内容可以省略 各子句严格遵守这个顺序 带搜索条件的查询 当我们需要对查询结果进行过滤的时候,可以将搜索条件放在 where 子句中\nSELECT * FROM student_info where number = 20220918; 如果我们需要指定某一列的值是否在某个列表中,可以使用 where in 子句\nSELECT * FROM student_info where number in (1, 2, 3); 带汇总函数的查询 count 函数 count 函数的含义是从查询的结果中统计出对应列中非 NULL 值的数量\n# 统计出结果中列 A 不为 null 的数量 select count(列 A) from 表名; # 统计结果中数据的数量,不管是否为 null select count(*) from 表名; select count(1) from 表名; # 统计结果中所有值不为 null 且数据不重复的数量 select count(distinct 列名) from 表名; 分组查询 当我们需要查询的结果进行分组,就可以使用 group by , group by 后跟的是需要分组的列\nselect ... from 表名 group by 列名 带 where 子句的分组查询,先进行筛选再进行分组\nselect ... from 表名 where 条件 group by 列名 有时候 group by 后会产生很多分组,如果想针对分组做过滤,就可以放到 having 子句中\n针对分组的过滤条件主要有:\n与分组列有关的条件,如:以科目分组, 那针对分组的过滤条件可以是 having subject = 'MySQL 课程' 这种条件也可以使用 where 子句进行过滤 与作用于分组的汇总函数有关的条件,如 having count(1) \u0026gt; 10 ,这个过滤条件就是过滤过分组后数据多于 10 条的分组 汇总函数有关的条件是不能出现在 where 子句,因为 where 过滤的条件是针对每一条记录,而汇总函数是针对某一分组中所有记录的情况,不适用于判断单条记录是否符合条件 select ... from 表名 group by 列名 having 条件 分组查询注意事项\n如果分组列中含有 NULL, 那么 NULL 也会作为一个独立的分组存在 group by 子句后可以跟表达式(但不能是汇总函数) 子查询 当我们想查询的结果涉及到多张表或查询必须由多条查询语句组成的时候就难免会使用到子查询\n什么是子查询?\n子查询一般由多个查询语句组成,小括号括起来的一条查询语句一般是一个子查询,在执行查询语句的时候,会按照小括号从内到外的顺序依次执行。所有子查询都必须使用小括号括起来。\n例子\nmysql\u0026gt; select * from student_score where number = (select number from student_info where name = \u0026#39;狗爷\u0026#39;); +----------+-----------+-------+ | number | subject | score | +----------+-----------+-------+ | 20220918 | 计算机 | 88 | +----------+-----------+-------+ 1 row in set (0.00 sec) 连接查询 把一个表的记录与另一个表的记录组合在一起形成新的更多的记录,这个过程称为连接查询\n# 两个表连接起来生成的结果集称为笛卡尔积,如果表 1 有 3 条记录,表 2 有 2 条记录,那组成后就含有 2 x 3 = 6 条记录,笛卡尔积就为 6 selecr * from 表1, 表2; 驱动表:根据 A 表中的数据去另一张表 B 中查数据,那么 A 表被称为驱动表,B 表称为被驱动表\n内连接 对于内连接的两个表,如果驱动表中记录在被驱动表中找不到匹配的记录,那这一条驱动表的记录不会加入到最后的结果集中\nselect * from t1 inner join t2 [on 过滤条件] 外连接 对于外连接的两个表,如果驱动表中记录在被驱动表中找不到匹配的记录,那这一条驱动表的记录仍会加入到最后的结果集中,对于不存的被驱动表的部分会使用 NULL 进行填充\n简单说就是驱动表的数据是全的,被驱动表不一定\n根据驱动表的不同,外连接可细分为左(外)连接、右(外)连接\n左(外)连接 # join 左侧的表称为外表或驱动表,右侧的表称为内表或被驱动表 select * from t1 left join t2 on 过滤条件 右(外)连接 # join 右侧的表为驱动表,左侧的表为被驱动表 select * from t1 right join t2 on 过滤条件 不管是左连接还是右连接,on 子句中的过滤条件是不能省略的 其余常用的sql语句 视图 当有些查询语句很复杂或很长的时候,此时如果想要复用这个语句就可以使用视图\n创建视图 create view 视图名 as 想要复用的查询语句; 视图的查询,更新,删除等语句都与正常真实的表的用法一致,视图其实就是相当于某个查询语句的别名\n在对视图进行查询的时候,MySQL服务器会将查询语句转换为真实的查询语句后才执行,使用视图的好处是简化书写\n如:select * from 视图名\n查看视图 # 视图是虚拟表,新创建的视图名不能与其他视图名或表名重复 show tables; # 查看视图的定义 show create view 视图名\\G 删除视图 drop view 视图名; 在执行 sql 语句的时候,有时候会出现 warning 警告,可以使用以下命令查询这个警告是什么\nshow warnings; 参考资料 MySQL基础教程 MySQL 8.0参考手册 MySQL是怎样使用的 快速入门MySQL ","permalink":"http://www.lmingyu.tech/posts/tech/mysql-learn-3/","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文记录的是 MySQL DML、DQL 常用的一些命令。\u003c/p\u003e\n\u003cp\u003eDML(Data Manipulation Language 数据操控语言) 用于操作数据库表对象中包含的数据\u003c/p\u003e\n\u003cp\u003eDQL (Data Query Language 数据查询语言 )用于查询数据\u003c/p\u003e\n\u003c/blockquote\u003e","title":"MySQL 基础-DML、DQL 命令"},{"content":" 在写业务代码的时候我们经常需要判断 2 个 Java 对象是否一样,常用判断对象是否相等可以使用 == 、equals、hashcode 这 3 个方法,本文试图搞清楚这三者的用法。\n关系操作符== 使用 == 判断 2 个对象是否相等,判断的是这 2 个对象的地址是否相等。\n示例代码:\npublic class Test { public static void main(String[] args) { Person jack = new Person(); Person tom = new Person(); Person bob = jack; System.out.println(\u0026#34;jack == tom ? \u0026#34; + (jack == tom)); System.out.println(\u0026#34;jack == bob ? \u0026#34; + (jack == bob)); System.out.println(\u0026#34;jack 地址: \u0026#34; + jack); System.out.println(\u0026#34;tom 地址: \u0026#34; + tom); System.out.println(\u0026#34;bob 地址: \u0026#34; + bob); } } 输出结果:\njack == tom ? false jack == bob ? true jack 地址: com.mingyu.javalearn.Person@1b2c6ec2 tom 地址: com.mingyu.javalearn.Person@4edde6e5 bob 地址: com.mingyu.javalearn.Person@1b2c6ec2 可以看出 jack 和 tom 虽然是 new 新建同一个 Person,但是新建出来的 Person 的对象地址是不一样的,所以 jack 是不等于 tom 的,可以从打印出来的地址结果看出 jack 与 bob 的对象地址一样,所以它俩比较的结果为 true\nEquals() 方法 Java 中所有类都有一个父类是 Object 这个类,每个类都会继承这个类的方法,包括 equals() 方法\nEquals() 的本质 继承父类的方法后,子类可以对父类的同名方法进行重写,在没有对 equals() 方法重写的情况下,使用 equals 方法判断 2 个对象是否相等与使用 上面所述的用 == 操作符判断结果是一样的,判断的都是这 2 个对象的地址是否相等\n示例代码:\npublic class Test { public static void main(String[] args) { Person jack = new Person(); Person tom = new Person(); Person bob = jack; System.out.println(\u0026#34;jack == tom ? \u0026#34; + (jack.equals(tom))); System.out.println(\u0026#34;jack == bob ? \u0026#34; + (jack.equals(bob))); } } 输出结果:\njack == tom ? false jack == bob ? true 其实看一下 equals() 方法的底层源码,其实使用的就是 == 对 2 个对象进行判断的\npublic boolean equals(Object obj) { return (this == obj); } 重写 equals() 方法 有时候我们不仅是要比较 2 个对象的地址是否一样,还需要判断这 2 个对象的内容是的一样,这个时候就需要对 equals 方法进行重写\n可以对 equals 进行以下的重写\n@Override public boolean equals(Object o) { // 检查是否为同一个对象的引用,如果是直接返回 true if (this == o) { return true; } // 检查是否是同一个类型,如果不是,直接返回 false if (o == null || getClass() != o.getClass()) { return false; } // 将 Object 对象进行转型 Person person = (Person) o; // 判断对象内容是否相等,这里判断的是 name 的内容 return Objects.equals(name, person.name); } 这样对对象的判断就会不一样,这次判断的是对象的内容是否一样\npublic class Test { public static void main(String[] args) { Person jack = new Person(\u0026#34;jack\u0026#34;); Person tom = new Person(\u0026#34;jack\u0026#34;); System.out.println(\u0026#34;jack == tom ? \u0026#34; + (jack.equals(tom))); System.out.println(\u0026#34;jack 地址: \u0026#34; + jack); System.out.println(\u0026#34;tom 地址: \u0026#34; + tom); } } 输出结果:\njack == tom ? true jack 地址: com.mingyu.javalearn.Person@1b2c6ec2 tom 地址: com.mingyu.javalearn.Person@4edde6e5 可以看出,jack 对象实例与 tom 对象实例虽然对象地址不一样,但是由于我们重写了 equals() 方法,使得比较的重点是对象的内容,所以比较的结果是 true\n其他 equals() 重写写法 有许多其他封装好的工具包都对equals()进行了重写\nApache Commons Lang 框架 @Override public boolean equals(Object o) { // 检查是否为同一个对象的引用,如果是直接返回 true if (this == o) { return true; } // 检查是否是同一个类型,如果不是,直接返回 false if (o == null || getClass() != o.getClass()) { return false; } // 将 Object 对象进行转型 Person person = (Person) o; // 判断对象内容是否相等,这里判断的是 name 的内容 return new EqualsBuilder().append(name, person.name).isEquals(); } equals 与 == 的区别 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 对于引用类型,== 判断两个变量是否引用的是同一个对象,而 equals() 没有重写时 ,和== 一样,重写之后判断引用的对象内容是否一样。 hashCode()方法 hashCode 也是 Object 类中定义的 方法,也可以比较两个对象是否相等,方法的返回值是调用对象的哈希值,这个哈希值的类型是 int\nhashCode() 的实现 我们看 Object 类的源码,可以发现 hashCode() 这个方法没有具体实现的,因为它是一个本地方法,是使用 C 系语言实现的,这个方法返回的哈希值是通过将对象的内存地址转换为整数得到的。\npublic native int hashCode(); 为什么需要 hashCode() 方法 上述说过的 equals 方法可以判断对象之间是否相等,为什么还需要 hashCode 方法呢?\n在源码的注释中提到了这个原因:\n支持这个方法是为了让哈希表受益,比如java.util.HashMap提供的哈希表 我们知道 HashSet 和 HashMap 等集合类在往集合中添加元素的时候,都会进行一个操作:判断当前需要加到集合的对象是都已经存放在当前集合中,这个时候就涉及到了对象之间的比较\n而 HashSet 和 HashMap 都使用了 hashCode() 方法来计算对象应该存储的位置,因此要将对象添加到这些集合类之前,都需要求出将要存储 key 的hashCode 值\n以下是 HashMap 源码中求 key 的 hashCode 值的方法\nstatic final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h \u0026gt;\u0026gt;\u0026gt; 16); } 那为什么要用 hashCode 方法,而不用 equals 方法呢?\n原因是调用 equals 方法更加耗时\n下面做了的实验\npublic class Test { public static void main(String[] args) { // 开始时间 long stime = System.currentTimeMillis(); // 计算执行时间 Person jack = new Person(); Person tom = new Person(); System.out.println(\u0026#34;jack == tom ? \u0026#34; + (jack.equals(tom))); // 结束时间 long etime = System.currentTimeMillis(); System.out.printf(\u0026#34;equals()执行时长:%d 毫秒.\u0026#34;, (etime - stime)); System.out.println(); System.out.println(\u0026#34;========== 明显的分割线 =========\u0026#34;); // 开始时间 stime = System.currentTimeMillis(); // 计算执行时间 Person mingyu = new Person(); Person bob = new Person(); System.out.println(\u0026#34;mingyu == bob ? \u0026#34; + (mingyu.hashCode() == bob.hashCode())); // 结束时间 etime = System.currentTimeMillis(); System.out.printf(\u0026#34;hashCode()执行时长:%d 毫秒.\u0026#34;, (etime - stime)); } } 输出结果:\njack == tom ? true equals()执行时长:3 毫秒. ========== 明显的分割线 ========= mingyu == bob ? false hashCode()执行时长:0 毫秒. 从输出结果可以看出调用 hashCode 方法几乎不耗时,因为本质上是比较的是 2 个 int 整型值,所以使用 hashCode 方法比较两个对象是很快的\n为什么需要 equals() 方法 既然 hashCode 方法这么快,为什么还需要 equals 方法呢?这是因为 hashCode 方法具有局限性\n局限性:两个对象的 hashCode 值相等并不代表两个对象就相等。\n这是因为 hashCode 值是通过哈希函数计算出来的,一般的计算过程是:通过对数组长度进行取模,这个的数组可以是内存数组、也可是集合数组,由于长度是有限的,就很难避免每次计算出来的哈希值都不一样,就会产出\u0026quot;哈希冲突\u0026quot;,越糟糕的哈希算法越容易产生冲突。\n哈希冲突对于比较两个对象是否相等是有影响的,即两个不同的对象哈希值也可能是一样的。\n所以单单通过 hashCode 方法是不够的,还需要使用 equals 方法进行进一步的判断\nHashSet 在添加元素到集合中的过程中就同时使用到这 2 个方法\n添加元素的步骤: 计算对象的 hashCode 值来判断对象加入的位置 与其他已经加入的对象的 hashCode 值作比较 如果没有一样的 hashCode 值,证明对象没有在当前集合中,因为两个相等的对象的 hashCode 值一定是相等 如果存在一样的 hashCode 值,再通过 equals 方法进行进一步判断 如果对象没有在当前集合中,就把对象存放进集合 这个过程通过先用 hashCode 方法进行判断,拦截了很多已经存在于集合中的元素调用 equals 方法的次数,提高程序执行的速度\nequals 与 hashCode 的关系 在《Effective Java》中写到:\n覆盖 equals 时总要覆盖 hashCode\n在每个重写了 equals 方法的类中,都必须重写 hashCode 方法\n如果只重写了 equals,没有重写 hashCode,当这个类的对象作为元素加到基于散列的集合(包括 HashMap、HashSet 和 Hashtable)中就会出现问题\n示例代码:\npublic class Test { public static void main(String[] args) { Person person1 = new Person(\u0026#34;jack\u0026#34;); Person person2 = new Person(\u0026#34;jack\u0026#34;); HashSet\u0026lt;Person\u0026gt; set = new HashSet\u0026lt;\u0026gt;(); set.add(person1); set.add(person2); System.out.println(\u0026#34;person1 equals person2: \u0026#34; + person1.equals(person2)); System.out.println(\u0026#34;person1.hashCode == person2.hashCode: \u0026#34; + (person1.hashCode() == person2.hashCode())); System.out.println(\u0026#34;person1 hashCode: \u0026#34; + person1.hashCode()); System.out.println(\u0026#34;person2 hashCode: \u0026#34; + person2.hashCode()); System.out.println(\u0026#34;set 集合元素个数: \u0026#34; + set.size()); for (Person person : set) { System.out.println(\u0026#34;person: \u0026#34; + person.getName()); } } } 输出结果:\nperson1 equals person2: true person1.hashCode == person2.hashCode: false person1 hashCode: 455896770 person2 hashCode: 1323165413 set 集合元素个数: 2 person: jack person: jack 由于没有重写 hashCode 方法,每次创建对象的时候,都会调用 Object 类的 hashCode 方法,该方法会生成不一样的哈希值\n而前面已经提到过,HashSet 判断一个对象是否存在当前集合,就是根据对象的哈希值判断的,所以会导致 equals 方法与 hashCode 对比的结果不一样,导致 HashSet 存在 2 个我们认为重复的对象\n所以在重写 equals 方法时,一定要同时重写 hashCode 方法\n那该如何重写 hashCode方法?\n重写应该遵循的原则: 如果对象的 equals 方法中比较时用到的信息没有被修改的话,那么多次调用这个对象的 hashCode 方法必须始终返回同一个值 如果 2 个对象 equals 方法比较时相等的,那这 2 个对象的 hashCode 方法都返回同样的结果 public class Person { private String name; @Override public boolean equals(Object o) { // 检查是否为同一个对象的引用,如果是直接返回 true if (this == o) { return true; } // 检查是否是同一个类型,如果不是,直接返回 false if (o == null || getClass() != o.getClass()) { return false; } // 将 Object 对象进行转型 Person person = (Person) o; // 判断对象内容是否相等,这里判断的是 name 的内容 return new EqualsBuilder().append(name, person.name).isEquals(); } @Override public int hashCode() { // 返回的是对 name 这个字段取哈希值 return Objects.hash(name); } } 输出结果:\nperson1 equals person2: true person1.hashCode == person2.hashCode: true person1 hashCode: 3254270 person2 hashCode: 3254270 set 集合元素个数: 1 person: jack 重写后两个对象的 hashCode 值一样,set 集合元素个数也是我们预想中的结果:1 个元素\n参考资料 On Java 中文版 基础卷 深入理解Java核心技术:写给Java工程师的干货笔记(基础篇) Effective Java中文版(第3版) ","permalink":"http://www.lmingyu.tech/posts/tech/java-learn-2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e在写业务代码的时候我们经常需要判断 2 个 Java 对象是否一样,常用判断对象是否相等可以使用 \u003ccode\u003e==\u003c/code\u003e 、equals、hashcode 这 3 个方法,本文试图搞清楚这三者的用法。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"这 2 个对象一样吗"},{"content":" 本文记录的是在使用命令行操作 MySQL 的一些常用命令。\n客户端与服务端相关命令 MySQL 分为客户端和服务端,客户端是发送真实访问的用户,就是需要操作数据库的人,服务端连接 MySQL 数据库,将客户端发送的操作命令发给数据库,然后将操作的结果再返回给用户。\n那为什么客户端不能直接连接数据库呢?\n那是因为一般客户端和 MySQL 数据库不会部署在同一台机器上,这时就不能直接访问数据库,而是必须带着用户名和密码,让服务端与数据库打交道(通常服务端一般会与数据库部署在同一台机器上,可以直接访问)\n启动命令 启动服务端 mysql.server start 指定端口启动 # 这里是大写 P,小写 p 代表密码 mysqld -P3307 启动客户端 # -p 与密码之间不能有空格; # 如果连接的是本地主机的数据库,-h 选项可以省略 # 各个参数的摆放顺序没有硬性要求 mysql -h 主机名 -u 用户名 -p密码 # 如果没有设置密码,启动客户端需要省略 -p 选项 mysql -u 用户名 一般在 -p 选项后面是不会直接输入密码的,因为其他人可以通过查询历史命令就知道了密码,所以通常是直接回车,然后再输入密码\n指定数据库启动客户端 mysql 数据库名 -u 用户名 -p 停止命令 停止服务端 mysql.server stop 停止退出客户端 # 以下 3 个命令都可以 quit exit \\q 查看版本 mysql -V 查看状态 在 MySQL 启动时需要确认当前启动的 MySQL 启动的状态是否与自己设置的配置一致,可以使用 status 命令\nmysql\u0026gt; status -------------- mysql Ver 8.0.28 for macos11 on arm64 (MySQL Community Server - GPL) Connection id:\t72 Current database: Current user:\troot@localhost SSL:\tNot in use Current pager:\tless Using outfile:\t\u0026#39;\u0026#39; Using delimiter:\t; Server version:\t8.0.28 MySQL Community Server - GPL Protocol version:\t10 Connection:\tLocalhost via UNIX socket Server characterset:\tutf8mb4 Db characterset:\tutf8mb4 Client characterset:\tutf8mb4 Conn. characterset:\tutf8mb4 UNIX socket:\t/tmp/mysql.sock Binary data as:\tHexadecimal Uptime:\t20 days 13 hours 33 min 26 sec Threads: 2 Questions: 598 Slow queries: 0 Opens: 282 Flush tables: 3 Open tables: 203 Queries per second avg: 0.000 -------------- mysql\u0026gt; 存储引擎 查看当前服务器程序所有支持的存储引擎 show engines; 查看当前设置的存储引擎 show variables like \u0026#39;default_storage_engine\u0026#39;; 创建表时指定表的存储引擎 create table 表名 ( 建表语句; ) engine = 存储引擎名称; 修改表的存储引擎 alter table 表名 engine = 存储引擎名称; 修改 MySQL 默认的存储引擎 # 仅对本次启动生效 mysqld --default-storage-engine=MyISAM; 参考资料 MySQL基础教程 MySQL 8.0参考手册 ","permalink":"http://www.lmingyu.tech/posts/tech/mysql-learn-1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文记录的是在使用命令行操作 MySQL 的一些常用命令。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"MySQL 基础-命令行相关命令"},{"content":" 记录一下自己在学习 Java 的一些笔记。\n重载 重载: 重载指的是方法的重载,重载的特征就是两个或多个方法具有同名。\n经典的重载方法 重载多用于构造器方法,因为构造器作用是初始化构建对象的,构造器的名字必须与类名相同,这就形成经典的无参构造器与有参构造器这一对重载方法。\n下面给出了示例:\n/** * @author mingyu */ public class User { private Integer id; private String userName; // 无参构造器 public User() { } // 单个参数的构造器 public User(Integer id) { this.id = id; } // 全参构造器 public User(Integer id, String userName) { this.id = id; this.userName = userName; } } 为什么需要重载 我们都知道,Java 中最重要的一个概念就是:对象。那方法就是对象的一个个行为。一个对象在做相同名字的行为也可以是在做不同的事情,比如:小明打麻将。\n字面意思:小明在和其他人一起玩麻将这类娱乐活动\n其他意思:小明在 \u0026ldquo;打\u0026rdquo; 一个名字叫 \u0026ldquo;麻将\u0026rdquo; 的人\n所以说,现实生活中语言当中的歧义也会出现在代码里。\n还比如说,一个人在吃东西,我给他什么,他就在吃什么,我给他面条,他的行为就是吃苗条,我给他米饭,他就是在吃米饭,但是他的行为都叫吃东西。\n总结:重载在代码当中是很必要的。\n重载的区分 如果不同的方法都是一个名字,要怎么分辨它们呢?\n分辨重载的重要原则:每个重载的方法必须有独一无二的参数类型列表(即每个方法都有与其他同名方法不同的入参)\n那不同的参数列表,到底哪里不同呢?\n参数类型的不同 public class Test { public static void main(String[] args) { // age 的类型不同可以区分两个同名的方法 getPerson(\u0026#34;mingyu\u0026#34;, 18); getPerson(\u0026#34;mingyu\u0026#34;, \u0026#34;18\u0026#34;); } static void getPerson(String name, int age) { System.out.println(\u0026#34;name: \u0026#34; + name + \u0026#34;, \u0026#34; + \u0026#34;int_age: \u0026#34; + age); } static void getPerson(String name, String age) { System.out.println(\u0026#34;name: \u0026#34; + name + \u0026#34;, \u0026#34; + \u0026#34;String_age: \u0026#34; + age); } } 输出结果:\nname: mingyu, int_age: 18 name: mingyu, String_age: 18 参数顺序的不同 虽然参数顺序不同可以区分同名的重载方法,但是不推荐使用这种使用方式进行区分,因为这样写代码很难维护\npublic class Test { public static void main(String[] args) { getPerson(\u0026#34;mingyu\u0026#34;, 18); getPerson(18,\u0026#34;mingyu\u0026#34;); } static void getPerson(String name, int age) { System.out.println(\u0026#34;name: \u0026#34; + name + \u0026#34;, \u0026#34; + \u0026#34;behind_age: \u0026#34; + age); } static void getPerson(int age, String name) { System.out.println(\u0026#34;name: \u0026#34; + name + \u0026#34;, \u0026#34; + \u0026#34;first_age: \u0026#34; + age); } } 输出结果:\nname: mingyu, behind_age: 18 name: mingyu, first_age: 18 返回值区分重载方法 除了参数列表的类型、顺序可以区分方法外,我们能否用方法的返回值来区分呢?\n答案是不行的,下面给出了例子。\nvoid f() {} int f() { return 1; } 当我们只想调用 f() 方法,但不需要该方法的返回值(如 System.out.println 方法),这时如果只是调用方法 f(),Java 是无法区分我们需要调用的是哪个方法的。\n基本类型的重载 对于只有基本类型不同的重载方法,会出现基本类型可以从小类型自动提升到较大类型的情况\npublic class Test { public static void main(String[] args) { int x = 5; checkBasicTypes(x); } static void checkBasicTypes(long x) { System.out.println(\u0026#34;Type long: \u0026#34; + x); } static void checkBasicTypes(float x) { System.out.println(\u0026#34;Type float: \u0026#34; + x); } static void checkBasicTypes(double x) { System.out.println(\u0026#34;Type double: \u0026#34; + x); } } 输出结果:\nType long: 5 变量 x 为 int 类型,但是 checkBasicTypes() 方法并没有 int 类型的入参,当程序运行时,会找到类型比 int 类型大的方法进行调用,即传入数据类型小于方法的参数类型,传入的数据类型会自动被提升。( int -\u0026gt; long)\n重写 重写一般出现在父子类关系之间,父类与子类有两个名称与参数列表都相同的方法,由于它们具有相同的方法名称,所以在方法调用的时候,子类的方法会覆盖同名的父类方法。\n下面是重写的一个例子\npublic class Test { public static void main(String[] args) { Animal animal = new Dog(); animal.say(); } } class Animal { public void say() { System.out.println(\u0026#34;I\u0026#39;m animal.\u0026#34;); } } class Dog extends Animal { @Override public void say() { System.out.println(\u0026#34;I\u0026#39;m Dog.\u0026#34;); } public void eat() { System.out.println(\u0026#34;Dog like eat meat.\u0026#34;); } } 输出结果:\nI\u0026#39;m Dog. 上面的例子中,父类与子类都定义了 say() 方法,实际上称之为子类重写了父类的 say()方法。\n重写方法之后,当我们调用子类对象的 say() 方法,尽管 Dog 对象的类型是 Animal ,Java 依然会调用 Dog 的 say() 方法,因为子类的方法会覆盖同名的父类方法。\n重写的原则 如果需要重写方法,需要满足里式替换原则:\n子类方法的访问权限必须大于等于父类方法 如父类方法修饰符为 protected,那子类方法的修饰符只能是 protected 或 public 子类方法的返回类型必须是父类方法返回类型或为其子类型。 如 B extends A : 父方法返回类型为 A,那么子类方法可以返回 A类型,也可以是 B 类型 子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。 @Override 注解 @Override 注解并不是 Java 中的关键字,但可以当做关键字一样使用,如果在重写方法上加上这个注解,编译器就会帮助你检查是否满足上面的三个限制条件,检测这个重写方法是否合法。有时候还可以有效的防止意外重载。\n参考资料 On Java 中文版 基础卷 深入理解Java核心技术:写给Java工程师的干货笔记(基础篇) ","permalink":"http://www.lmingyu.tech/posts/tech/java%E5%9F%BA%E7%A1%80-1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e记录一下自己在学习 Java 的一些笔记。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"重载与重写"},{"content":" 用好系列就是记录自己日常如何使用好软件工具,此文为用好系列-IDEA之插件篇,用于记录自己日常开发编程时IDEA常用的插件,此系列会持续更新。本文只记录插件本身,不记录插件使用方法,若记录使用方法,会导致内容长度过长,不方便阅览,使用方法可自行谷歌学习。\n外观类 外观是第一生产力\nNyan Progress Bar 让你 IDEA 里面的进度条变得更可爱\n插件地址\nXcode-Dark Theme 换过很多的主题,还是这个主题用的最舒服\n插件地址\nRainbow Brackets 给你的括号加上不同颜色,加以区分\n插件地址 CodeGlance Pro 给你的代码增加缩略图,可以快速查找代码的位置\n插件地址\nExtra ToolWindow Colorful Icons 丰富你的工具窗口图标\n插件地址\nExtra Icons 丰富文件夹图标,这个是需要 IDEA 商业版才能使用\n插件地址\n工具类 Auto filling Java call arguments 在创建方法或构造方法会自动填充方法的所有参数\n使用方法:alt + Enter\n插件地址\nGenerateAllSetter 一键调用一个对象的所有的set方法,在给实体类设置值时很实用\n使用方法:alt + Enter\n插件地址\nGrep Console 通过expression表达式过滤日志、给不同级别的日志加上你想要的颜色。\n插件地址\nGsonFormatPlus 可以快速将 Json 转换为 Java 的类\n插件地址\nJRebel and XRebel 一款热部署 JVM 插件,通过跳过耗时的构建和重新部署步骤,简化了 Java 应用程序本地调试开发,这插件需要激活才能使用,可自行谷歌搜索一下\n插件地址\nMaven Helper 可以使用 Maven Helper 插件的 Dependency Analyzer 来分析工程的多级依赖关系,解决依赖冲突问题,在项目的 pom.xml文件下方打开 Dependency Analyzer 进行分析\n插件地址\nMyBatisX MybatisX 是一款基于 IDEA 的快速开发插件,包括以下比较好用的功能如:XML 跳转、自动生成代码(根据数据库表结构生成实体类和一些基本的mapper)、重置模板、JPA 提示等\n插件地址\n插件使用介绍\nString Manipulation 主要可以用于字符串处理:大小写切换、排序、转义、格式调整(对齐、删除空行)、加解密等。使用 Alt + M 快捷键快速操作,功能十分强大。\n插件地址\nTranslation 使用过笔记好用的翻译,是阅读源码的一个利器,个人使用的是阿里的翻译引擎,使用阿里开发账号即可激活\n插件地址\n锦上添花类 Alibaba Java Coding Guidelines(XenoAmess TPM) 是根据阿里的 Java 开发手册生成的一个代码规范插件,帮助我们写出更规范的代码\n插件地址\nSonarLint 同样是一个代码检插件,经常会提示一些更好的代码写法,推荐使用。\n插件地址\nSpotBugs 会帮助你找出代码中一些潜在的代码 BUG,但是它分析出来的也不能全信,还是要自己分析\n插件地址\nAlibaba Cloud AI Coding Assistant 阿里出的代码推荐插件,根据你敲出的几个字母给你推荐你可能需要的代码,最喜欢它的一个功能是代码文档搜索,它可以帮你找到已经开源的一些 API 接口的写法。\n像这种 AI 推荐代码的插件还有很多,AiXcoder Code Completer、Tabnine AI Code Completion- JS Java Python TS Rust Go PHP \u0026amp; More,选择自己喜欢就可以,但是不要沉迷代码工具,这些工具只能辅助,代码还是得自己写。\n插件地址\n取舍与优化 插件虽好,但不要贪多哦\n当你下载了很多插件使用的时候,难免会对 IDEA 软件运行和代码编译会造成影响,这时候就要进行适当的取舍和优化\n我们可以用 IDEA 本身有的功能分析每个插件对项目启动的影响,对于不使用的插件可以进行禁用。\nHelp -\u0026gt; Diagnostic Tools -\u0026gt; Analyze Plugin Startup Performance\n可以适当提供 IDEA 软件使用内存的上限,设置完之后重启即可生效。\nHelp -\u0026gt; Diagnostic Tools -\u0026gt; Analyze Plugin Startup Performance\n","permalink":"http://www.lmingyu.tech/posts/tech/%E7%94%A8%E5%A5%BDidea-1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e用好系列就是记录自己日常如何使用好软件工具,此文为用好系列-IDEA之插件篇,用于记录自己日常开发编程时IDEA常用的插件,此系列会持续更新。本文只记录插件本身,不记录插件使用方法,若记录使用方法,会导致内容长度过长,不方便阅览,使用方法可自行谷歌学习。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"用好系列-IDEA之插件篇"},{"content":" 做事总是三分钟热度,是一种病吗\n\t我往往在某一段时间都会有一些工作以外想做的东西,学一门外语、学会做菜、学吉他、学一门技术等等。一开始会兴致勃勃的查教程、查攻略,看各种视频,问他人推荐该怎样开始会少走一些弯路\u0026hellip; \u0026hellip;\n经过一点时间收集之后,等我认为所有事前准备都 OK 的时候,万事具备只欠开始时,我却犹豫了。又或者我刚开始这件事情的时候遇到很多需要自己独自解决的事情,众多的困难如拦路虎一般挡住我前进的道路。很多事情出发点是好的,对自己有益处的,但是就是在开始的时候会给自己的信心浇上一盆冷水,而且这种情况经常发生。\n在背单词的时候,永远只能背到 a 字母开头的;\n在学一门技术的时候,往往只能看到入门的地方就没有了下文;\n健身持续了几周之后,然后因为难以持续大肚子它又回来了;\n刷算法题,刷的那几道熟悉的题都滚瓜烂熟了,但新题往往觉得很难,就不想刷了\n\u0026hellip; \u0026hellip;\n我尝试分析一下原因:每件事情我一开始就在自己内心定下了一个很大的目标,每次完成这件事情之后,不仅身体觉得很累,心也很累,到下次开始做这件事的时候,身心都感到了抗拒,最后便无疾而终。\n最近看到刘未鹏写的《暗时间》第 2 版,这里面有给到我启发,做一件事,最难的是开始并持续下去,过早的退出,是一切失败的根源。\n\u003c!DOCTYPE HTML\u003e 保持做一件事情,兴趣是很关键的一件因素。但是对我们这种好像对什么事情都提不起兴趣的人该怎么办呢?\n我觉得,让自己保持在一个舒服的状态才能一直做一件事情,想想我们每天都做过一些不是生活必须的事情,是不是都让我们无感、甚至很舒服,至少不会让自己感到难受,比如:\n吃完饭会习惯散步走一圈\n每天习惯性看一下热搜,关心一下今天都发生什么事情\n每天做一次核酸(我是无感的,但是有些人就是觉得核酸对他生活造成了影响)\n这些日常都会做的事情,不是自己兴趣所在,但为什么能持续地做呢,我总结就是:这些事情对你的生活不造成侵入,让你保持在一个还比较舒服的状态。基于这个结论,我可以以一个比较舒服的状态去做每一件事情,每天只背 5 个单词,每天只刷一道题,每周学做一道菜\u0026hellip;\n但是这样做事情,效率不是很慢吗?但其实认真想想,这些背单词、学吉他、学技术,每天学 10 分钟,从几年前开始,到现在为止,我已经可以把这些事情完成的很好了。\n所以,从量少入手,让浪花变成巨浪吧。\n","permalink":"http://www.lmingyu.tech/posts/life/1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e做事总是三分钟热度,是一种病吗\u003c/p\u003e\n\u003c/blockquote\u003e","title":"让自己舒服才能持续下去"},{"content":" 常用系列就是记录自己编程常用的东西,此文为常用系列第 2 篇,用于记录自己日常常用的 Git 命令,以便需要时查找。Git 尽管提供了 GUI 界面进行操作,但是我觉得 Git 的 GUI 软件有很多,但是掌握最常用的命令还是一个合格的程序员应该做的,毕竟命令到哪里都一样,但 GUI 软件万一哪一天用不了了呢🐶,而且命令行模式可以执行 Git 的所有命令。\nGit 最基本的操作 文件从本地到仓库流程:以下记录一下用哪些命令可以将一个文件从自己的本地推送到远端的仓库,我把这称为基本操作\n安装 Git 如果想要在 Mac 电脑安装 Git,只需要输入查询 git 版本的命令就会提示你安装(如果你之前没装过的话)\ngit --version 配置用户信息 安装完 git 之后对 git 的环境进行配置,这些配置在每台计算机只需要配置一次,当然配置完以后也可以修改,git 最常用的配置就是对用户名和邮箱地址的设置,--global代表对系统上所有仓库都生效。\n# 设置用户签名,可在 .gitconfig 文件中查看 git config --global user.name \u0026lt;用户名\u0026gt; # 设置用户邮箱,可在 .gitconfig 文件中查看 git config --global user.email \u0026lt;注册git仓库邮箱\u0026gt; 获取 Git 仓库 通常获取仓库有两种方式:\n将本地目录转换为 Git 仓库 从别的地方复制一个仓库到你的本地 在已存在目录中初始化仓库 git init 这个命令会在当前目录下创建一个.git的目录,目录中包括初始化仓库的所有文件\n克隆仓库 git clone https://github.com/libgit2/libgit2 这个命令会从远端 GitHub 的 libgit2 仓库拉取所有东西到你当前目录\n记录文件状态 文件的状态有 2 种:已被跟踪、未被跟踪,跟踪的意思是现在 git 可以记录这个文件发生的所有变化(被修改、被删除、重命名)\n查询文件状态 git status # 简洁输出文件状态,A-先添加到暂存区、M-修改过的文件、D-删掉的文件、R-重命名的文件、??-未被跟踪的文件 git status -s 跟踪新文件 # 使用以下命令之后,一般都会再使用 git status 查看文件是否被跟踪 git add \u0026lt;文件名\u0026gt; # 将所有待加入暂存区的文件加入暂存区 git add . 使用该命令之后,文件就会被放入 git 的暂存区,暂存区里面的文件都是即将要推送到远端仓库的候选者。\n对比已暂存的文件与未暂存文件之间的区别 通常我们修改文件过多时就会容易忘记某个文件修改过的内容,这个时候就需要查看我们做过哪些修改就可以使用diff命令\n# 文件需在暂存区,即被跟踪才能使用本命令 git diff \u0026lt;文件名\u0026gt; 如果你想对比暂存区里的文件与上次提交时文件的区别,就可以使用以下命令\ngit diff --cached 提交更新 如果你的暂存区里面已经包括你本次想要提交的文件,就可以通过commit命令对文件的修改进行提交\n# 每次commit都会清空暂存区 git commit -m \u0026#34;\u0026lt;双引号里面填的是本次提交文件的一个描述信息\u0026gt;\u0026#34; 推送文件到远端 添加远端仓库 如果你不是从远端仓库克隆 clone 的文件,而是自己在本地自己创建的 git 仓库,就需要在向远端仓库推送之前加上你要推送的仓库\ngit remote add https://github.com/paulboone/ticgit 推送 git push origin master 这个命令代表着将 master 分支的代码推送到远端的 origin 服务器\n到此一个文件从创建到最终同步到远端仓库的这个过程就圆满完成。\n下面就记录一下在使用 git 的过程中遇到一些情况该使用哪些命令。\n查询 这里的记录是使用 git 场景下各种需要查的情况\n查提交历史 整个项目历史提交记录 git log commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon \u0026lt;[email protected]\u0026gt; Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon \u0026lt;[email protected]\u0026gt; Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test # 以一行的形式展示过往所有的提交 git log --pretty=oneline # 分支提交图 git log --graph git log --graph --pretty=oneline --abbrev-commit 输出的内容依次是:\n每个提交的 SHA-1 校验和 提交人的名字 邮箱地址 提交时间 提交说明 文件的历史提交记录 查看单个文件每次的提交记录\ngit log -p \u0026lt;文件名\u0026gt; # 只显示最近 2 次提交 git log -p -2 \u0026lt;文件名\u0026gt; 每一行的提交记录 能显示任何文件中每行最后一次修改的提交记录\ngit blame \u0026lt;文件名\u0026gt; # 限定显示哪一行 git blame -L 69,82 \u0026lt;文件名\u0026gt; 查找BUG 很多时候,当前分支出现问题了,但不清楚哪里出现了问题,就可以使用git bisect命令查找哪一次代码提交引入了错误\n这个命令使用的原理是二分查找的原理:\n通过二分【代码是没问题的提交 ~ 当前有问题的分支】这个范围找到可能有问题的那一次提交\ngit bisect start命令启动查错,它的格式如下:\n$ git bisect start [终点] [起点] 终点是最近的提交,起点是更久以前的提交。它们之间的这段历史,就是差错的范围\n当不清楚哪个起点提交时没问题的时候可以选择最开始的分支\n# 终点是当期分支 HEAD,起点是第一次提交 4d83cf git bisect start HEAD 4d83cf 执行上面的命令以后,当前的代码就会切换到这段范围当中中间的那一次提交\n然后你就可以对当前代码进行测试,如果是没有问题就执行以下命令对本次提交进行标识\ngit bisect good 如果没问题就意味着错误是在代码历史的后半段引入的。执行上面的命令,Git 就自动切换到后半段的中点\n然后再进行测试,如果是有问题的就标识这个提交是有问题的\ngit bisect bad 到这里不是就结束了哦,有问题只是这次有问题,但我们需要找到第一次有问题的提交\n接下来,不断重复这个过程,直到成功找到出问题的那一次提交为止。这时,Git 会给出如下的提示。\nb47892 is the first bad commit 然后就可以去分析这次都提交了哪些文件,从而分析错误是什么原因引起的\n注意:代码缺陷需要自己去判断的,git 是没办法帮你分析出哪里有问题\n然后,使用git bisect reset命令,退出查错,回到最近一次的代码提交。\n查找内容 由于 git 与 Linux命令一样,同样可以与 grep搭配使用\ngit grep -n \u0026#34;\u0026#34; 撤销 我们在操作 Git 的时候难免会出现出错的时候,修改错误的办法一般不是重新修改就是回退到没问题的时候\n文件的撤销 将文件尚未加入暂存区的修改全部撤销 # -- 很重要,没有--,就变成了“切换到另一个分支”的命令 git checkout -- \u0026lt;文件名\u0026gt; # 恢复暂存区的所有文件到工作区 git checkout . # 恢复 commit 的指定文件到暂存区和工作区 git checkout [commit] [file] 将工作区相对于暂存区的修改撤销,如果暂存区没有对应的文件则回退到HEAD指向的版本 git restore \u0026lt;文件名\u0026gt; 将暂存区的文件修改撤销掉(unstage),重新放回工作区 git reset HEAD \u0026lt;文件名\u0026gt; 版本的撤销 常用于提交版本之后,远端发生冲突无法 merge 分支,用git resrt回退版本处理完冲突后再进行推送\n# 将commit过的代码库回滚到上一个版本 git reset --hard HEAD^ 或 git reset --hard HEAD~ # 往上回滚两次版本,以此类推 git reset --hard HEAD^^: # 往上回滚100个版本 git reset --hard HEAD~100: # 回滚到某一特定版本,版本号为前7位,使用git reflog可以查出 git reset --hard 版本号: 分支操作 分支基本操作 # 列出所有本地分支,当前分支前面会标一个*号 $ git branch # 列出所有远程分支 $ git branch -r # 列出所有本地分支和远程分支,并详细展示分支信息 $ git branch -av # 新建一个分支,并切换到该分支 $ git checkout -b [branch] # 切换到指定分支,并更新工作区 $ git checkout [branch-name] # 切换到上一个分支 $ git checkout - # 删除分支 $ git branch -d [branch-name] merge 用于合并两个分支\n# 合并指定分支到当前分支 $ git merge [branch] rebase 也用于分支合并\n合并分支的操作与merge基本一样,\n# 合并指定分支到当前分支 git rebase [branch] 如果git rebase遇到冲突:\n第一步手动解决冲突,然后把新修改的文件加入暂存区 git add . 之后并不需要 git commit,而是直接运行git rebase --continue 至于git merge 与 git rebase有什么区别呢?\ngit merge合并完之后会显示出来合并之前的分支,而git rebase合并之后看起来并不会有之前合并的分支记录 分支合并的顺序也不一样 详细可以看一下这个博客 git merge 和 git rebase 小结\ncherry-pick 合并指定提交到当前分支\n# 选择一个 commit,合并进当前分支 git cherry-pick [commit] # 合并多次提交到当前分支 git cherry-pick \u0026lt;HashA\u0026gt; \u0026lt;HashB\u0026gt; # 合并联系一段的提交,A 提交必须早于 B,提交 A 不会包含本次提交 git cherry-pick A..B # 合并联系一段的提交,A 提交必须早于 B,本次提交包含提交 A git cherry-pick A^..B 如果 cherry-pick过程中发送冲突,则在手动处理完冲突之后使用--continue命令让过程继续\n解决代码冲突后,将修改的文件重新加入暂存区git add . 使用下面的命令,让 Cherry pick 过程继续执行。 git cherry-pick --continue 参考资料 Git 官网\ngit bisect 命令教程\ngit cherry-pick 教程\nGit 教程-开发者手册\n一个一步一步显示 git 命令的网站\n","permalink":"http://www.lmingyu.tech/posts/tech/git%E5%91%BD%E4%BB%A4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e常用系列就是记录自己编程常用的东西,此文为常用系列第 2 篇,用于记录自己日常常用的 Git 命令,以便需要时查找。Git 尽管提供了 GUI 界面进行操作,但是我觉得 Git 的 GUI 软件有很多,但是掌握最常用的命令还是一个合格的程序员应该做的,毕竟命令到哪里都一样,但 GUI 软件万一哪一天用不了了呢🐶,而且命令行模式可以执行 Git 的\u003cstrong\u003e所有命令\u003c/strong\u003e。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"常用系列-Git命令"},{"content":" 本文记录如何使用 Surge 将家中的 Mac 电脑成为家中其他设备科学上网的中转站。\n前情提要 今年 618 大促入手人生第一台掌机游戏机 Nintendo Switch OLED 版(日版),Switch 游戏可以从eshop 中下载,但是国内直接访问下载 eshop 中的游戏速度可以说是龟速。得知 Switch 中 eshop 需要和 Steam 一样开启游戏加速器才能正常使用。但是 UU 加速器使用包年需要 283 RMB,就不舍得(对,我就是那种买贵东西不眨眼,但是买便宜附属品时斤斤计较的人🙄)。\n我记起来之前看过其他人用 Surge 把 Mac 当中代理转发让 Apple TV 可以顺利地访问(科学上网),我到网上查找教程,果然真的可以,于是就有了这篇教程,也是做一个记录,以便以后翻阅。\n前期环境准备 Surge for Mac (通过 Mac 电脑作为中间商为其他设备作为代理转发) Mac Studio (接着网线的,配置好之后是不能关机的,我试过 Mac 电脑休眠之后 Surge 软件是会自动退出的,所以需要保持电脑不要休眠,如果休眠了需要重新开启才能继续使用) M1 Max 运存:64 G 系统:masOS Monterey 12.4 机场订阅地址(没有的话需要自己找一下😂,科学上网必备) 以下教程默认已经准备好以上的 3 个条件\nSurge Surge 介绍 总览 Surge 是一个网络开发和代理工具。他是为开发者打造的工具,所以使用时需要具备一定的专业知识。\n以下四种是 Surge 的核心工作流。\n接管:接管设备发起的网络连接。Surge 支持代理服务器和虚拟网卡两种接管模式。 处理:可以修改已经接管的网络请求和响应。包括 URL 重定向、本地文件映射、使用 JavaScript 自定义修改等多种方法。 转发:可以将已接管的网络请求转发到其他代理服务器。这可以是全局转发,也可以通过灵活的规则系统来确定出站策略。 拦截:可以拦截并保存网络请求和响应的数据,也可以通过 MITM 解密 HTTPS 流量。 我们这里主要使用的是转发\n特点 我使用 Surge 这个软件主要是被它这几个特点吸引的:\n高性能、稳定、高效率:Surge 可以在耗费极少资源并且保持高稳定性的前提下轻松处理网络数据,配合 M1 芯片更加节能和稳定 增强模式:Surge 会设置一个虚拟网络去接管所有的网络请求,尤其是那些对 Web 代理支持不好的软件。 网关模式:Surge Mac 可以当做三层网关去处理同网络下其他设备的网络请求。(简单来说就可以当做一个路由器,成为家中网络枢纽) Surge 下载与购买 下载 Surge for Mac 可以直接在 Surge 官网下载,我下载的版本是最新版,Surge 4.6.1 (1718) ,下载完之后是一个压缩包,解压之后把 app 拖到应用程序中即可打开使用了。\n但是下载之后并不能直接科学上网的,还是需要花钱进行购买,到官网进行授权购买,购买 1 台设备授权 49.99 美刀,3 台设备授权 69.99 美刀,5 台设备授权 99.99 美刀,支持支付宝付款,这里我买的是 3 台设备授权(我是想在过年回家也可以给家里也弄一个就买的3台),实花 471.13 RMB (根据当时汇率波动,心疼啊),可以根据个人需求进行购买,至少购买 1 台。\n购买之后就可以根据购买时的邮箱地址和购买之后给你邮箱发的激活码对 Surge 进行激活。\nSurge 接管 Mac 网络 接下来就可以让 Surge 接管 Mac 电脑的网络了(让 Mac 可以科学上网),这时候就需要你有机场的订阅地址了。\n往 Surge 中添加配置文件\n这里使用的是从 URL 安装配置文件,你也可以通过文件进行导入或者自己手动添加配置。\n如果你订阅的机场是直接有 Surge 的订阅地址可以直接添加,如果没有,但是有v2ray、ss 、trojan 的订阅也是可以的,通过第三方订阅转换生成 Surge 可以识别的 URL 。\n选择基础模式,如果你想自定义你想要的特定节点可以选择进阶模式 在订阅链接中粘贴上你订阅机场的订阅地址 在客户端下拉框中选择 Surge4 点击红色生成订阅链接就可以生成定制订阅 URL,点击复制 打开 Surge 主界面左下角更多,选择设置中的配置\n选择从 URL 安装配置文件\n粘贴你刚才由第三方生成的配置文件订阅地址,然后选择完成,等待一会 Surge 就会对规则策略进行导入\n等 Surge 识别完成之后会出现一个新的托管配置,选中这个新的配置,并点击右下角应用\n回到 Surge 主界面,选中代理下的策略,选择 规则判定 ,这时检测一下科学上网是否成功了,如果不行,点击标签栏的 Surge 检查一下是否勾选了 设置为系统代理 和 增强模式,没有勾选可以勾选上,并重启一下 Surge\n到此你的 Mac 就可以愉快地上网了\n让 Mac 成为家中网络枢纽 上面的步骤已经可以让 Mac 电脑愉快地上网了,但是独乐了不如众乐乐,那怎么让家里其他设备也可以愉快地上网呢?\n这里就需要用到 Surge 的 DHCP 服务器这个功能了,也就是上面介绍过的网关模式。\n设置电脑 IP 为静态IP 打开 系统偏好设置-网络,选择以太网(也就是网线接口) 配置 IPv4 选择 使用 DHCP(手动设定地址) 我的路由器是 192.168.1.1 所以 IP 地址我就填 192.168.1.2,只用修改最后一位在 2-225 之间就行。(如果你的路由器是 192.168.0.1 你的 IP 地址就可以填 192.168.0.2,以此类推) 随后选择 应用 注意:这里设置之前先把 Surge 设置为系统代理先关掉,开启会影响后面 ip 地址设置,建议先关掉,后面设置完之后再开启。\n关闭路由器 DHCP 登录到你们家的路由器管理后台,在管理页面中找到 DHCP 服务器设置,把路由器的 DHCP 关掉(一般路由器都会有这个选项)\n打开 Surge DHCP 选项 在Surge左侧 设备 里,点开后最下面有 DHCP 服务器 开关,点开后选择下一步。\n插网线的选择 Ethernet ,点击下一步。\nSurge 会检测当前网络环境是否有 DHCP 设置,如果上面你正确关闭了 路由器 DHCP 这里应该能直接生成配置\n选择 Surge 默认的设置点击完成即可,如果你发现这里路由地址为空(我一开始在这里卡了很久,后面发现选错了网络设备,选成了Ethernet Adapter (en6)),你就要检查一下哪里步骤出错了\n启动完 DHCP 服务器之后可以看到设备列表没有任何设备,这时需要重启一下路由器或者让你想使用的设备重新连接一下wifi,就可以在新增设备中看到\n添加设备成员 想要科学上网的设备可以右键选择设备名,然后选择 使用 Surge 作为网关,然后设备重新连接wifi或者关闭路由器 wifi 再打开即可。\n没连接上会显示等待连接,这里建议把设置固定 IP 地址也选上,这时官网推荐的做法,方便以后设备管理和连接。\n苹果手机和平板,由于注重隐私的原因会默认在连接 Wifi 的时候选上使用私有无线局域网地址进行连接(使用私有地址有助于减少不同无线局域网对 iPhone 的跟踪),这里需要把这个选项关掉重新连接 wifi 才能让 Surge 识别出你的设备。\n如果不清楚 IP 地址具体对应哪台设备,可以到路由器管理后台查看或者根据此 IP 的制造商进行推断。\n最后推荐将设备连接Wifi的IPV4地址改为手动,因为你如果你启用了自动DHCP的话,有可能下次连接这个WIFI时IP地址就会改变,如果连接失败,可以按照以下的配置将IPV4地址改为手动即可。\n至此,家里的想要一起科学上网的设备都可以通过 Mac 电脑这个网络枢纽一起愉快玩耍了!\n特殊情况 一开始使用的时候,我遇到过用surge作为网关的 Mac 可以打开网页的 youtube 视频并且能观看,但是家里其他设备通过wifi连接的缺不能访问 google,检查一下是可以打开 youtube App,但是只能看到视频的缩略图,进一步点击打开视频观看,却始终是 loading状态,无法观看。奇怪的是可以打开Netflix,正常看剧。\n这种情况查找到解决的办法是,在你需要访问 YouTube 的设备上把连接的 wifi 的 DNS配置修改一下,从自动改为手动,并添加一个 DNS 服务器 192.0.2.2\n至于为什么设置为这个DNS地址我也不是很清楚,只知道手册上是这样写的🐶,这样就把 YouTube 不能访问的问题就解决了!\n总结 以后有其他设备想要愉快上网,都可以通过这台被 Surge 附魔的 Mac 电脑作为转发代理,省下了加速器的年费,还比加速器好使,覆盖范围更广,但需要的是这台 Mac 电脑要一直开着,不然家里其他的设备都上不了网。\n这篇文章只是简单使用了 Surge 的简单功能(规则访问策略并没有定制化),用户还可以对 Surge 进行很多定制化的改造,这些更深入的功能就等以后慢慢发掘吧,先去愉快地玩我新到的 Switch 啦,有什么好玩的游戏,欢迎推荐!\n参考资料 Surge 把你的 Mac 变成最强路由器 Surge 使用手册 Surge 教程 Surge 论坛 家庭网络部署心得 ","permalink":"http://www.lmingyu.tech/posts/tech/%E5%88%A9%E7%94%A8surge%E6%8A%8Amac%E5%8F%98%E6%88%90%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C%E6%9E%A2%E7%BA%BD/","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文记录如何使用 Surge 将家中的 Mac 电脑成为家中其他设备科学上网的中转站。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"利用Surge把Mac变成家中网络枢纽"},{"content":" 常用系列就是记录自己编程常用的东西,此文为常用系列第 1 篇,用于记录自己日常开发编程时常用的 Linux 命令,以便需要时查找。\n文件操作命令 查看目录 ls # 回到上一个目录 cd - # 显示所有文件 ls -a # 显示目录中的文件按照修改时间排序 ls -lht tree 展示当前目录的文件结构\n# 展示当前目录的文件结构 tree -FC / -a:展示隐藏文件 -C: 颜色显示 -L 2: 只显示2层 -F: 显示目录后面的\\;显示可执行文件*;功能类似ls -F 修改文件权限 # 修改文件权限 chmod # 给xxx添加可写权限,常用于给需要运行的脚本赋予权限 chmod +x xxx # 给xxx添加可读权限 chmod +r xxx # 将xxx的权限改成777 ,所有人都对这个文件可读可写可运行 chmod 777 xxx # 递归修改整个文件夹的权限 chmod 777 xxx -R 查看文件内容 more # 浏览文件内容 more \u0026lt;filename\u0026gt; 回车:下一行 空格:下一页 # 页面进度增加36% b:上一页 q:退出 less # 与more类似,功能更全 less 回车:下一行 y:上一行 Page Down:下一页 Page Up:上一页 q:退出 tail # 展示文件末尾3行内容 tail -3 \u0026lt;filename\u0026gt; # 常用于观察日志写入过程 tail -f -n 1000 \u0026lt;filename\u0026gt; 管道相关 wc # 输出结果依次为统计行数、单词数、字节数 wc wc -l:统计行数 wc -w:统计单词数 wc -c:统计字节数 grep # 在文件中过滤出给定的match_patten grep match_patten \u0026lt;file\u0026gt; 常用参数 -c 统计文件中包含文本的次数 -n 打印匹配的行号 -i 搜索时忽略大小写 # 从stdin中读入若干行数据,如果某行中包含xxx,则输出该行。--color代表高亮显示 grep xxx --color # 在多级目录中对文本递归搜索(程序员搜代码的最爱) grep \u0026#34;class\u0026#34; . -R -n # 找到使用过的命令 history | grep \u0026lt;命令\u0026gt; xargs xargs 能够将输入数据转化为特定命令的命令行参数;这样,可以配合很多命令来组合使用。比如 grep,find;\nxargs参数说明 -d 定义定界符 (默认为空格 多行的定界符为 n) -n 指定输出为多行 -I {} 指定替换字符串,这个字符串在xargs扩展时会被替换掉,用于待执行的命令需要多个参数时 -0:指定0为输入定界符 将多行输出转化为单行输出 cat file.txt| xargs 将单行转化为多行输出,-n:指定每行显示的字段数 cat single.txt | xargs -n 3 cat file.txt | xargs -I {} ./command.sh -p {} -1 cut 用于从每行中删除部分内容\n#分割一行内容,从stdin中读入多行数据 示例: echo $PATH | cut -d \u0026#39;:\u0026#39; -f 3,5 输出PATH用:分割后第3、5列数据 echo $PATH | cut -d \u0026#39;:\u0026#39; -f 3-5 输出PATH用:分割后第3-5列数据 echo $PATH | cut -c 3,5 输出PATH的第3、5个字符 echo $PATH | cut -c 3-5 输出PATH的第3-5个字符 查找文件 locate 按照路径名查找文件,查找的范围在 /var/lib/mlocate/ 这个数据库中,存储在内存中,每天更新一次,所以无法用 locate 搜索新建的文件,可以使用 updatedb 来立即更新数据库。\n系统如果没有自带locate命令,可以使用yum install mlocate -y安装,安装结束执行updatedb命令\n# 查找名称含有 zip 文件 lcoate zip find命令 以文件名查找文件\n# 搜索某个文件路径下的所有*.py文件 find /path/to/directory/ -name \u0026#39;*.py\u0026#39; # 忽略大小写搜索 find /home -iname \u0026#34;*.txt\u0026#34; # 当前目录及子目录下查找所有以.txt 和.pdf 结尾的文件 find . -name \u0026#34;*.txt\u0026#34; -o -name \u0026#34;*.pdf\u0026#34; # 查看当前目录下文件个数 find ./ | wc -l # 按类型搜索,只列出所有目录,-type f 文件 / l 符号链接 / d 目录 find . -type d -print # 限定查找文件的大小,+1M 代表查找大于 1M 的文件 find ./ -type f -name \u0026#34;*.py\u0026#34; -size +1M # 删除符合条件的文件,下面的命令能够当前删除当前目录下后缀为 .bak 的文件 find ./ -type f -name \u0026#34;*.bak\u0026#34; -delete sed文本替换 sed一般用于对文本内容做替换,操作时是逐行操作\nsed的基本工作方式是:\n将文件以行为单位读取到内存(这个内存也叫模式空间) 使用sed的每个脚本对该行进行操作 处理完成后输出该行 格式\nsed ‘操作命令’ 一个或多个文件\nsed的替换命令s: sed \u0026#39;s/旧的字符串/新的字符串/\u0026#39; 文件名 sed接多个指令 sed -e \u0026#39;s/旧的字符串/新的字符串/\u0026#39; -e \u0026#39;s/旧的字符串/新的字符串/\u0026#39; 文件名 首处替换 sed \u0026#39;s/text/replace_text/\u0026#39; file //替换每一行的第一处匹配的text 全局替换 sed \u0026#39;s/text/replace_text/g\u0026#39; file 默认替换后,输出替换后的内容,如果需要直接替换原文件,使用-i: sed -i \u0026#39;s/text/repalce_text/g\u0026#39; file 移除空白行 sed \u0026#39;/^$/d\u0026#39; file 第一个匹配的括号内容使用标记 1 来引用 sed \u0026#39;s/hello\\([0-9]\\)/\\1/\u0026#39; file Bash键盘技巧 # 将光标移到行首第一个字符 Ctl + a # 将光标移到行首第一个字符 Ctl + e # 删除光标到行首的所有字符 Ctl + u # 常用: # 删除输入的内容 Ctl + e + Ctl + u 归档与备份 gzip 压缩单个或多个文件\ngzip foo.text gunzip 解压文件\n# 压缩,生成foo.txt.gz gunzip foo.text zip 压缩和归档成 .zip 文件\nzip -r text.zip text unzip 解压 .zip 的文件\nunzip text.zip tar 归档多个文件或目录\n# 打包与 gzip 配合 tar -zcvf \u0026lt;打包压缩后的文件名\u0026gt; \u0026lt;要打包压缩的文件\u0026gt; z:调用 gzip 压缩命令进行压缩 c:打包文件 v:显示运行过程 f:指定文件名 # 解压 tar -zxvf test.tar.gz -C /usr**(- C 代表指定解压的位置) x:代表解包 磁盘管理 查看磁盘空间 # 查看磁盘空间利用大小:-h: human缩写,以易读的方式显示结果 df -hT # 查看当前目录所占空间大小,-s 递归整个目录的大小 du -sh # 查看当前目录下所有子文件夹排序后的大小: for i in `ls`; do du -sh $i; done | sort 或者: du -sh `ls` | sort 进程管理工具 查询进程 ps # 查询正在运行的进程信息: # VSZ-虚拟内存大小 RSS-该进程占用的 RAM 数量 ps -aux/ ps -ef top # 显示进程信息,并实时更新 top 打开后,输入M:按使用内存排序 打开后,输入P:按使用CPU排序,查看系统中使用CPU、使用内存最多的进程 打开后,输入i:使top不显示任何闲置或者僵死进程 打开后,输入q:退出 lsof # 查看端口占用的进程状态: lsof -i:3306 # 查看用户username的进程所打开的文件 lsof -u username # 查询init进程当前打开的文件 lsof -c init 其他 # 查询进程ID(适合只记得部分进程字段) pgrep -l \u0026lt;进程名\u0026gt; # 查看占用端口的进程 netstat -anp | grep port 终止进程 # 杀死指定PID的进程 (PID为Process ID) kill PID # 杀死相关进程 kill -9 3434 # 杀死job工作 (job为job number) kill %job 分析线程栈 使用命令pmap,来输出进程内存的状况,可以用来分析线程堆栈;\npmap PID 性能监控 监控CPU # 查看CPU使用率 sar -u eg: $sar -u 1 2 后面的两个参数表示监控的频率,比如例子中的1和2,表示每秒采样一次,总共采样2次; # 查看CPU平均负载,sar指定-q后,就能查看运行队列中的进程数、系统上的进程大小、平均负载等; sar -q 1 2 查询内存 # 查看内存使用状况 sar指定-r之后,可查看内存使用状况; sar -rh 1 2 # 查看内存使用量 free -mh 查询端口占用 # 查看当前系统端口使用情况 netstat -an # 展示进程加被占用的端口 netstat -ntlp 查询页面交换 查看页面交换发生状况 页面发生交换时,服务器的吞吐量会大幅下降;服务器状况不良时,如果怀疑因为内存不足而导致了页面交换的发生,可以使用sar -W这个命令来确认是否发生了大量的交换;\nsar -W 1 3 综合应用 当系统中sar不可用时,可以使用以下工具替代:linux下有 vmstat、Unix系统有prstat\neg: 查看cpu、内存、使用情况: vmstat n m (n 为监控频率、m为监控次数)\nvmstat 1 3 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 86560 42300 9752 63556 0 1 1 1 0 0 0 0 99 0 1 0 86560 39936 9764 63544 0 0 0 52 66 95 5 0 95 0 0 0 86560 42168 9772 63556 0 0 0 20 127 231 13 2 84 0 使用watch 工具监控变化 当需要持续的监控应用的某个数据变化时,watch工具能满足要求; 执行watch命令后,会进入到一个界面,输出当前被监控的数据,一旦数据变化,便会高亮显示变化情况;\neg:操作redis时,监控内存变化:\nwatch -d -n 1 \u0026#39;./redis-cli info | grep memory\u0026#39; (以下为watch工具中的界面内容,一旦内存变化,即实时高亮显示变化) Every 1.0s: ./redis-cli info | grep memory Mon Apr 28 16:10:36 2014 used_memory:45157376 used_memory_human:43.07M used_memory_rss:47628288 used_memory_peak:49686080 used_memory_peak_human:47.38M 系统网络状况 ping 向指定的主机发送 ICMP 数据包,用于确认与主机的网络连接。发送的时间间隔为 1 s,用 Ctrl + C中断发送,正常网络会显示 0% 的分组丢失率\n# 检查是否连网 ping www.baidu.com traceroute 会列出网络从本地到注定主机经过的所有跳数\n# 会显示经过路由器的主机名、IP地址及本地到该路由器3个往返时间采样数据 traceroute -I www.baidu.com ip 是一个多功能的网络配置工具,用于检查系统的网络接口和路由表\n会显示多个网络接口,常见接口: lo:环回接口,是一个虚拟接口,系统用于 “和自己对话” eth0: 以太网接口 接口第一行如果含有 UP字样,表明该接口已启用 第3行 inet 字段为该网络接口的 IP 地址 # ~$ ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:16:3e:0e:a0:8b brd ff:ff:ff:ff:ff:ff inet 172.30.140.229/20 brd 172.30.143.255 scope global dynamic eth0 valid_lft 310094870sec preferred_lft 310094870sec inet6 fe80::216:3eff:fe0e:a08b/64 scope link valid_lft forever preferred_lft forever 3: docker0: \u0026lt;NO-CARRIER,BROADCAST,MULTICAST,UP\u0026gt; mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:53:b2:cd:34 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever netstat 用于检查各种网路设置和统计信息,需安装net-tools yum install net-tools\n# 检查系统的网络接口 netstat -ie # 查看所有网络连接 netstat -nt ssh 用于本地主机与远程主机进行通信\n# 需要输入 remote-sys 主机 ubuntu 用户的登录密码 ssh ubuntu@remote-sys 其他常用命令 # 常与别名命令 alias 一起使用,=号两边不能有空格,被别名的命令需要用单引号包含 alias ll=\u0026#39;ls -alh\u0026#39; # 查看两个文件间的差别: diff file1 file2 # 查看带有SVN的环境变量值: env | grep SVN 参考资料 Linux基础 - Linux Tools Quick Tutorial\nLinux命令手册\n工作中常用的的 Linux 命令\nLinux 命令搜索\n","permalink":"http://www.lmingyu.tech/posts/tech/linux%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e常用系列就是记录自己编程常用的东西,此文为常用系列第 1 篇,用于记录自己日常开发编程时常用的 Linux 命令,以便需要时查找。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"常用系列-Linux 常用命令"},{"content":"","permalink":"http://www.lmingyu.tech/links/","summary":"","title":""},{"content":" 名字 Taiming Liang 坐标 广东 深圳 职业 后端开发工程师 描述 努力往独立开发者 💻 迈进 💪 尝试以英语为切入点重新认识世界 🌍 想以非功利的方式去阅读 📖 很高兴认识你! ─=≡Σ(((つ•̀ω•́)つ)) ","permalink":"http://www.lmingyu.tech/about/","summary":"名字 Taiming Liang 坐标 广东 深圳 职业 后端开发工程师 描述 努力往独立开发者 💻 迈进 💪 尝试以英语为切入点重新认识世界 🌍 想以非功利的方式去阅读 📖 很高兴认识你! ─=","title":"👨💻个人画像"}]