精华内容
下载资源
问答
  • 腾讯微博三周年谋局:从未放弃过争夺第一.docx
  • 我为什么放弃Go语言

    万次阅读 多人点赞 2014-04-14 19:24:19
    有好几次,当我起来的时候,总是会问自己:这个决定是正确的吗?是明智和理性的吗?其实我一直在认真思考这个问题。开门见山地说,我当初放弃Go语言,就是因为两个“不爽”:第一,对Go语言本身不爽;第二,对Go...

    我为什么放弃Go语言

    作者:庄晓立(Liigo)

    日期:2014年3月

    原创链接:http://blog.csdn.net/liigo/article/details/23699459

    转载请注明出处:http://blog.csdn.net/liigo

    有好几次,当我想起来的时候,总是会问自己:我为什么要放弃Go语言?这个决定是正确的吗?是明智和理性的吗?其实我一直在认真思考这个问题。

    开门见山地说,我当初放弃Go语言(golang),就是因为两个“不爽”:第一,对Go语言本身不爽;第二,对Go语言社区里的某些人不爽。毫无疑问,这是非常主观的结论。但是我有足够详实的客观的论据,用以支撑这个看似主观的结论。

    第-1节:更新日志(倒叙)

    Liigo 2021-07-13 补记14:今日HN热帖上大伙普遍吐槽channel,要用好真的很难。judofyr: "Channels are extremely subtle in my experience". laumars: "It took me a loooong time to fully wrap my head around channels. They’re definitely not explicit and full of subtle yet devastating bugs when used inappropriately (which is all to easy to do too)." 💡 另将本文更新日志从文末移至文首作为第-1节,并改为按日期倒叙排列。

    Liigo 2020-2-19 补记13:本文末尾增加Golang 2.0泛型相关信息。

    Liigo 2016-5-15 补记12:文中第2节(我为什么对Go社区的人不爽)增加netroby和D语言联合创始人的现身说法。补充说明Go 1.5后不再使用C语言开发。

    Liigo 2016-3-3 补记11:文中1.5节(垃圾回收器/GC)末尾增加一段,再次提示开发者重视Go语言GC的潜在问题。

    Liigo 2015-7-17 补记10:文中1.11节(泛型)增加来自HN的延伸阅读链接,开发者们抱怨Go欠缺泛型支持。

    Liigo 2015-7-3 补记9:文中1.5节(垃圾回收器/GC)增加 “Stop the world” 相关的三个链接。

    Liigo 2015-7-2 补记8:前两天网上出现了一篇本文的驳文,《驳狗屎文 "我为什么放弃Go语言"》,作者是chai2010,请读者们参照阅读,顺便领略一下本文后半部分描述过的Go粉的"风采"。(这篇驳文至少有两个好处:1 它全文引用了本文(便于读者参照);2 使用了本文当时的最新版(包含了补记7)。)

    Liigo 2015-6-2 补记7:补充两篇英文:Why Go Is Not Good(作者Will Yager重点批评了Go语言的设计不佳甚至是倒退),Leaving Go(作者Danny Gratzer放弃Go语言的原因主要是:没有泛型,充满黑魔法)。这两篇文章都是针对具体问题做具体分析的,与本文写作精神一致,务实不务虚。其中提到的对Go语言不满的地方,本文也多有涉及,结论类似。

    Liigo 2015-5-29 补记6:补充说明Go语言直到2015年下半年1.5发布后才将GOMAXPROCS设置为大于1的默认值(HN),他们文中承认之前一直默认设置为1是因为调度器不完善(与我此文最初发表时的猜测一致)。

    Liigo 2015-4-1 补记5:文中1.10(黑魔法)和1.12(接口)章节增加了两处“延伸阅读”链接,被引用的链接后面均有大量网友评论。此举主要是为了说明本文观点并非一家之言。

    Liigo 2015-1-31 补记4:全世界认为Go语言不好的可不只是我Liigo一个人。国外著名的问答网站Quora上面有个人气很高的提问,“为什么不要用Go语言”(英文网页),看看那排名最前的两个答案,以及广大程序员们给这两个答案的数百个“赞”,都足以说明Go语言自身的问题是客观存在的。人民群众的眼睛是雪亮的。

    Liigo 2014-4-29 补记3:Go语言的拥护者们,似乎连Go语言的“核心优势”都说不出几条。知乎上很有人气的一条问答《为什么要使用 Go 语言,Go 语言的优势在哪里》,连静态编译、GC、跨平台都拿出来说了(无视C/C++/Java),甚至连简单易学(无视Python/易语言)、“丰富的”标准库(跟谁比?敢跟Java/C#/Python比么?)、好用的工具链(gofmt)都扯出来了,可见除了“并发、网络”之外,他们也讲不出另外的什么核心优势了,只能靠一些周边的东西凑数。

    Liigo 2014-4-29 补记2:著名的编程语言研究专家王垠写了一篇《对 Go 语言的综合评价》(晚于本博文发表约三五天),也是总体上持批判态度,看衰Go语言。读者们可以对照阅读。

    Liigo 2014-4-29 补记1:Go语言社区还有一个很奇特的现象,就是中国社区独大,国外社区要小的多。有外国网友还专门写了一篇文章研究《为什么Golang中国社区独大》这个问题(文中也提到了我这篇博文)。通常来说,在IT和软件领域,向来都是国外先进国家引领技术潮流,然后国内缓慢跟进。而到了Go语言这里,恰恰反过来了,似乎暗示着在国外的主流软件开发技术人员并不怎么待见Go语言,Go只是在国内受到一帮人的盲目推崇而已,至于这帮人的眼光如何,反正我不看好。

    第0节:我的Go语言经历

    先说说我的经历吧,以避免被无缘无故地当作Go语言的低级黑。

    2009年底,Go语言(golang)第一个公开版本发布,笼罩着“Google公司制造”的光环,吸引了许多慕名而来的尝鲜者,我(Liigo)也身居其中,笼统的看了一些Go语言的资料,学习了基础的教程,因对其语法中的分号和花括号不满,很快就遗忘掉了,没拿它当一回事。

    两年之后,2011年底,Go语言发布1.0的计划被提上日程,相关的报道又多起来,我再次关注它,[重新评估][1]之后决定深入参与Go语言。我订阅了其users、nuts、dev、commits等官方邮件组,坚持每天阅读其中的电子邮件,以及开发者提交的每一次源代码更新,给Go提交了许多改进意见,甚至包括[修改Go语言编译器源代码][2]直接参与开发任务。如此持续了数月时间。

    到2012年初,Go 1.0发布,语言和标准库都已经基本定型,不可能再有大幅改进,我对Go语言未能在1.0定型之前更上一个台阶、实现自我突破,甚至带着诸多明显缺陷走向1.0,感到非常失望,因而逐渐疏远了它(所以Go 1.0之后的事情我很少关心)。后来看到即将发布的Go 1.1的Release Note,发现语言层面没有太大改变,只是在库和工具层面有所修补和改进,感到它尚在幼年就失去成长的动力,越发失望。外加Go语言社区里的某些人,其中也包括Google公司负责开发Go语言的某些人,其态度、言行,让我极度厌恶,促使我决绝地离弃Go语言。

    在上一个10年,我(Liigo)在我所属的公司里,深度参与了两个编程语言项目的开发。我想,对于如何判断某个编程语言的优劣,或者说至少对于如何判断某个编程语言是否适合于我自己,我应该还是有一点发言权的。

    [1]: https://plus.google.com/+LiigoZhuang/posts/CpRNPeDXUDW

    [2]: http://blog.csdn.net/liigo/article/details/7467309

    第1节:我为什么对Go语言不爽?

    Go语言有很多让我不爽之处,这里列出我现在还能记起的其中一部分,排名基本上不分先后。读者们耐心地看完之后,还能淡定地说一句“我不在乎”吗?

    1.1 不允许左花括号另起一行

    关于对花括号的摆放,在C语言、C++、Java、C#等社区中,十余年来存在持续争议,从未形成一致意见。在我看来,这本来就是主观倾向很重的抉择,不违反原则不涉及是非的情况下,不应该搞一刀切,让程序员或团队自己选择就足够了。编程语言本身强行限制,把自己的喜好强加给别人,得不偿失。无论倾向于其中任意一种,必然得罪与其对立的一群人。虽然我现在已经习惯了把左花括号放在行尾,但一想到被禁止其他选择,就感到十分不爽。Go语言这这个问题上,没有做到“团结一切可以团结的力量”不说,还有意给自己树敌,太失败了。

    1.2 编译器莫名其妙地给行尾加上分号

    对Go语言本身而言,行尾的分号是可以省略的。但是在其编译器(gc)的实现中,为了方便编译器开发者,却在词法分析阶段强行添加了行尾的分号,反过来又影响到语言规范,对“怎样添加分号”做出特殊规定。这种变态做法前无古人。在左花括号被意外放到下一行行首的情况下,它自动在上一行行尾添加的分号,会导致莫名其妙的编译错误(Go 1.0之前),连它自己都解释不明白。如果实在处理不好分号,干脆不要省略分号得了;或者,Scala和JavaScript的编译器是开源的,跟它们学学怎么处理省略行尾分号可以吗?

    1.3 极度强调编译速度,不惜放弃本应提供的功能

    程序员是人不是神,编码过程中免不了因为大意或疏忽犯一些错。其中有一些,是大家集体性的很容易就中招的错误(Go语言里的例子我暂时想不起来,C++里的例子有“基类析构函数不是虚函数”)。这时候编译器应该站出来,多做一些检查、约束、核对性工作,尽量阻止常规错误的发生,尽量不让有潜在错误的代码编译通过,必要时给出一些警告或提示,让程序员留意。编译器不就是机器么,不就是应该多做脏活累活杂活、减少人的心智负担么?编译器多做一项检查,可能会避免数十万程序员今后多年内无数次犯同样的错误,节省的时间不计其数,这是功德无量的好事。但是Go编译器的作者们可不这么想,他们不愿意自己多花几个小时给编译器增加新功能,觉得那是亏本,反而减慢了编译速度。他们以影响编译速度为由,拒绝了很多对编译器改进的要求。典型的因噎废食。强调编译速度固然值得赞赏,但如果因此放弃应有的功能,我不赞成。

    1.4 错误处理机制太原始

    在Go语言中处理错误的基本模式是:函数通常返回多个值,其中最后一个值是error类型,用于表示错误类型极其描述;调用者每次调用完一个函数,都需要检查这个error并进行相应的错误处理:if err != nil { /*这种代码写多了不想吐么*/ }。此模式跟C语言那种很原始的错误处理相比如出一辙,并无实质性改进。实际应用中很容易形成多层嵌套的if else语句,可以想一想这个编码场景:先判断文件是否存在,如果存在则打开文件,如果打开成功则读取文件,如果读取成功再写入一段数据,最后关闭文件,别忘了还要处理每一步骤中出现错误的情况,这代码写出来得有多变态、多丑陋?实践中普遍的做法是,判断操作出错后提前return,以避免多层花括号嵌套,但这么做的后果是,许多错误处理代码被放在前面突出的位置,常规的处理逻辑反而被掩埋到后面去了,代码可读性极差。而且,error对象的标准接口只能返回一个错误文本,有时候调用者为了区分不同的错误类型,甚至需要解析该文本。除此之外,你只能手工强制转换error类型到特定子类型(静态类型的优势没了)。至于panic - recover机制,致命的缺陷是不能跨越库的边界使用,注定是一个半成品,最多只能在自己的pkg里面玩一玩。Java的异常处理虽然也有自身的问题(比如Checked Exceptions),但总体上还是比Go的错误处理高明很多。

    1.5 垃圾回收器(GC)不完善、有重大缺陷

    在Go 1.0前夕,其垃圾回收器在32位环境下有内存泄漏,一直拖着不肯改进,这且不说。Go语言垃圾回收器真正致命的缺陷是,会导致整个进程不可预知的间歇性停顿(Stop the World)。像某些大型后台服务程序,如游戏服务器、APP容器等,由于占用内存巨大,其内存对象数量极多,GC完成一次回收周期,可能需要数秒甚至更长时间,这段时间内,整个服务进程是阻塞的、停顿的,在外界看来就是服务中断、无响应,再牛逼的并发机制到了这里统统失效。垃圾回收器定期启动,每次启动就导致短暂的服务中断,这样下去,还有人敢用吗?这可是后台服务器进程,是Go语言的重点应用领域。以上现象可不是我假设出来的,而是事实存在的现实问题,受其严重困扰的也不是一家两家了(2013年底ECUG Con 2013京东的刘奇提到了Go语言的GC、defer、标准库实现是性能杀手,最大的痛苦是GC;美团的沈锋也提到Go语言的GC导致后台服务间隔性停顿是最大的问题。更早的网络游戏仙侠道开发团队也曾受Go垃圾回收的沉重打击)。在实践中,你必须努力减少进程中的对象数量,以便把GC导致的间歇性停顿控制在可接受范围内。除此之外你别无选择(难道你还想自己更换GC算法、甚至砍掉GC?那还是Go语言吗?)。跳出圈外,我近期一直在思考,一定需要垃圾回收器吗?没有垃圾回收器就一定是历史的倒退吗?(可能会新写一篇博客文章专题探讨。)

    2016年3月3日Liigo补记:直到2015年底,Go 1.5新GC发布后数月,仍获知有大陆圈内知名团队因为GC的原因考虑换掉Go语言,颇有感触。当软件系统逐步发展到更庞大更复杂的时候,Go语言的垃圾回收器(GC)就变成了指不定啥时候会出现的拦路虎,让人进退两难。进,暂时没有确切有效的技术手段对付响应延迟和内存暴涨;退,多年开发付出的心血付之东流损失惨重。语言选型之前多做调查分析,如果一定要用Go语言开发,控制系统规模和复杂度,避开底层的核心业务,可能是比较明智的选择。

    1.6 禁止未使用变量和多余import

    Go编译器不允许存在被未被使用的变量和多余的import,如果存在,必然导致编译错误。但是现实情况是,在代码编写、重构、调试过程中,例如,临时性的注释掉一行代码,很容易就会导致同时出现未使用的变量和多余的import,直接编译错误了,你必须相应的把变量定义注释掉,再翻页回到文件首部把多余的import也注释掉,……等事情办完了,想把刚才注释的代码找回来,又要好几个麻烦的步骤。还有一个让人蛋疼的问题,编写数据库相关的代码时,如果你import某数据库驱动的pkg,它编译给你报错,说不需要import这个未被使用的pkg;但如果你听信编译器的话删掉该import,编译是通过了,运行时必然报错,说找不到数据库驱动;你看看程序员被折腾的两边不是人,最后不得不请出大神:`import _`。对待这种问题,一个比较好的解决方案是,视其为编译警告而非编译错误。但是Go语言开发者很固执,不容许这种折中方案。

    1.7 创建对象的方式太多令人纠结

    创建对象的方式,调用new函数、调用make函数、调用New方法、使用花括号语法直接初始化结构体,你选哪一种?不好选择,因为没有一个固定的模式。从实践中看,如果要创建一个语言内置类型(如channel、map)的对象,通常用make函数创建;如果要创建标准库或第三方库定义的类型的对象,首先要去文档里找一下有没有New方法,如果有就最好调用New方法创建对象,如果没有New方法,则退而求其次,用初始化结构体的方式创建其对象。这个过程颇为周折,不像C++、Java、C#那样直接new就行了。

    1.8 对象没有构造函数和析构函数

    没有构造函数还好说,毕竟还有自定义的New方法,大致也算是构造函数了。没有析构函数就比较难受了,没法实现RAII。额外的人工处理资源清理工作,无疑加重了程序员的心智负担。没人性啊,还嫌我们程序员加班还少吗?C++里有析构函数,Java里虽然没有析构函数但是有人家finally语句啊,Go呢,什么都没有。没错,你有个defer,可是那个defer问题更大,详见下文吧。

    1.9 defer语句的语义设定不甚合理

    Go语言设计defer语句的出发点是好的,把释放资源的“代码”放在靠近创建资源的地方,但把释放资源的“动作”推迟(defer)到函数返回前执行。遗憾的是其执行时机的设置似乎有些不甚合理。设想有一个需要长期运行的函数,其中有无限循环语句,在循环体内不断的创建资源(或分配内存),并用defer语句确保释放。由于函数一直运行没有返回,所有defer语句都得不到执行,循环过程中创建的大量短暂性资源一直积累着,得不到回收。而且,系统为了存储defer列表还要额外占用资源,也是持续增加的。这样下去,过不了多久,整个系统就要因为资源耗尽而崩溃。像这类长期运行的函数,http.ListenAndServe()就是典型的例子。在Go语言重点应用领域,可以说几乎每一个后台服务程序都必然有这么一类函数,往往还都是程序的核心部分。如果程序员不小心在这些函数中使用了defer语句,可以说后患无穷。如果语言设计者把defer的语义设定为在所属代码块结束时(而非函数返回时)执行,是不是更好一点呢?可是Go 1.0早已发布定型,为了保持向后兼容性,已经不可能改变了。小心使用defer语句!一不小心就中招。

    1.10 许多语言内置设施不支持用户定义的类型

    for in、make、range、channel、map等都仅支持语言内置类型,不支持用户定义的类型(?)。用户定义的类型没法支持for in循环,用户不能编写像make、range那样“参数类型和个数”甚至“返回值类型和个数”都可变的函数,不能编写像channel、map那样类似泛型的数据类型。语言内置的那些东西,处处充斥着斧凿的痕迹。这体现了语言设计的局限性、封闭性、不完善,可扩展性差,像是新手作品——且不论其设计者和实现者如何权威。延伸阅读:Go语言是30年前的陈旧设计思想,用户定义的东西几乎都是二等公民(Tikhon Jelvis)。

    1.11 没有泛型支持,常见数据类型接口丑陋

    没有泛型的话,List、Set、Tree这些常见的基础性数据类型的接口就只能很丑陋:放进去的对象是一个具体的类型,取出来之后成了无类型的interface{}(可以视为所有类型的基础类型),还得强制类型转换之后才能继续使用,令人无语。Go语言缺少min、max这类函数,求数值绝对值的函数abs只接收/返回双精度小数类型,排序接口只能借助sort.Interface无奈的回避了被比较对象的类型,等等等等,都是没有泛型导致的结果。没有泛型,接口很难优雅起来。Go开发者没有明确拒绝泛型,只是说还没有找到很好的方法实现泛型(能不能学学已经开源的语言呀)。现实是,Go 1.0已经定型,泛型还没有,那些丑陋的接口为了保持向后兼容必须长期存在着。延伸阅读:HN网友抱怨Go没有泛型

    1.12 实现接口不需要明确声明

    这一条通常是被当作Go语言的优点来宣传的。但是也有人不赞同,比如我。如果一个类型用Go语言的方式默默的实现了某个接口,使用者和代码维护者都很难发现这一点(除非仔细核对该类型的每一个方法的函数签名,并跟所有可能的接口定义相互对照),自然也想不到与该接口有关的应用,显得十分隐晦,不直观。支持者可能会辩解说,我可以在文档中注明它实现了哪些接口。问题是,写在文档中,还不如直接写到类型定义上呢,至少还能得到编译器的静态类型检查。缺少了编译器的支持,当接口类型的函数签名被改变时,当实现该接口的类型方法被无意中改变时,实现者可能很难意识到,该类型实现该接口的隐含约束事实上已经被打破了。又有人辩解说,我可以通过单元测试确保类型正确实现了接口呀。我想说的是,明明可以通过明确声明实现接口,享受编译器提供的类型检查,你却要自己找麻烦,去写原本多余的单元测试,找虐很爽吗?Go语言的这种做法,除了减少一些对接口所在库的依赖之外,没有其他好处,得不偿失。延伸阅读:为什么我不喜欢Go语言式的接口(老赵)。

    1.13 省掉小括号却省不掉花括号

    Go语言里面的if语句,其条件表达式不需要用小括号扩起来,这被作为“代码比较简洁”的证据来宣传。可是,你省掉了小括号,却不能省掉大括号啊,一条完整的if语句至少还得三行吧,人家C、C++、Java都可以在一行之内搞定的(可以省掉花括号)。人家还有x?a:b表达式呢,也是一行搞定,你Go语言用if else写至少得五行吧?哪里简洁了?

    1.14 编译生成的可执行文件尺寸非常大

    记得当年我写了一个很简单的程序,把所有系统环境变量的名称和值输出到控制台,核心代码也就那么三五行,结果编译出来把我吓坏了:EXE文件的大小超过4MB。如果是C语言写的同样功能的程序,0.04MB都是多的。我把这个信息反馈到官方社区,结果人家不在乎。是,我知道现在的硬盘容量都数百GB、上TB了……可您这种优化程度……怎么让我相信您在其他地方也能做到不错呢。(再次强调一遍,我所有的经验和数据都来自Go 1.0发布前夕。)

    1.15 不支持动态加载类库

    静态编译的程序当然是很好的,没有额外的运行时依赖,部署时很方便。但是之前我们说了,静态编译的文件尺寸很大。如果一个软件系统由多个可执行程序构成,累加起来就很可观。如果用动态编译,发布时带同一套动态库,可以节省很多容量。更关键的是,动态库可以运行时加载和卸载,这是静态库做不到的。还有那些LGPL等协议的第三方C库受版权限制是不允许静态编译的。至于动态库的版本管理难题,可以通过给动态库内的所有符号添加版本号解决。无论如何,应该给予程序员选择权,让他们自己决定使用静态库还是动态库。一刀切的拒绝动态编译是不合适的。

    1.16 其他

    • 不支持方法和函数重载(overload)
    • 导入pkg的import语句后边部分竟然是文本(import ”fmt”)
    • 没有enum类型,全局性常量难以分类,iota把简单的事情复杂化
    • 定义对象方法时,receiver类型应该选用指针还是非指针让人纠结
    • 定义结构体和接口的语法稍繁,interface XXX{} struct YYY{} 不是更简洁吗?前面加上type关键字显得罗嗦。
    • 测试类库testing里面没有AssertEqual函数,标准库的单元测试代码中充斥着if a != b { t.Fatal(...) }
    • 语言太简单,以至于不得不放弃很多有用的特性,“保持语言简单”往往成为拒绝改进的理由。
    • 标准库的实现总体来说不甚理想,其代码质量大概处于“基本可用”的程度,真正到企业级应用领域,往往就会暴露出诸多不足之处。
    • 版本都发展到1.2了,goroutine调度器依旧默认仅使用一个系统线程。GOMAXPROCS的长期存在似乎暗示着官方从来没有足够的信心,让调度器正确安全地运行在多核环境中。这跟Go语言自身以并发为核心的定位有致命的矛盾。(直到2015年下半年1.5发布后才有改观
    • 官方发行版中包含了一个叫oracle的辅助程序,与Oracle数据库毫无关系,却完全无视两者之间的名称混淆。

    上面列出的是我目前还能想到的对Go语言的不爽之处,毕竟时间过去两年多,还有一些早就遗忘了。其中一部分固然是小不爽,可能忍一忍就过去了,但是很多不爽积累起来,总会时不时地让人难受,时间久了有自虐的感觉。程序员的工作生活本来就够枯燥的,何必呢。

    必须要说的是,对于其中大多数不爽之处,我(Liigo)都曾经试图改变过它们:在Go 1.0版本发布之前,我在其官方邮件组提过很多意见和建议(甚至包括提交代码CL),极力据理力争,可以说付出很大努力,目的就是希望定型后的Go语言是一个相对完善的、没有明显缺陷的编程语言。结果是令人失望的,我人微言轻、势单力薄,不可能影响整个语言的发展走向。1.0之前,最佳的否定自我、超越自我的机会,就这么遗憾地错过了。我最终发现,很多时候不是技术问题,而是技术人员的问题。

    第2节:我为什么对Go语言的某些人不爽?

    这里提到的“某些人”主要是两类:一、负责专职开发Go语言的Google公司员工;二、Go语言的推崇者和脑残粉丝。我跟这两类人打过很多交道,不胜其烦。再次强调一遍,我指的是“某些”人,而不是所有人,请不要对号入座。

    Google公司内部负责专职开发Go语言的核心开发组某些成员,他们倾向于闭门造车,固执己见,对第三方提出的建议不重视。他们常常挂在嘴边的口头禅是:现有的做法很好、不需要那个功能、我们开发Go语言是给Google自己用的、Google不需要那个功能、如果你一定要改请fork之后自己改、别干提意见请提交代码。很多言行都是“反开源”的。通过一些具体的例子,还能更形象的看清这一层。就留下作为课后作业吧。

    我最不能接受的就是他们对1.0版本的散漫处理。那时候Go还没到1.0,初出茅庐的小学生,有很大的改进空间,是全面翻新的最佳时机,彼时不改更待何时?1.0是打地基的版本,基础不牢靠,等1.0定型之后,处处受到向后兼容性的牵制,束手缚脚,每前进一步都阻力重重。急于发布1.0,过早定型,留下诸多遗憾,彰显了开发者的功利性强,在技术上不追求尽善尽美。

    Go语言的核心开发成员,他们日常的开发工作是使用C语言——Go语言的编译器和运行时库,包括语言核心数据结构和算法map、channel、scheduler,都是C开发的——真正用自己开发的Go语言进行实际的大型应用开发的机会并不多。虽然标准库是用Go语言自己写的,但他们却没有大范围使用标准库的经历。实际上,他们缺少使用Go语言的实战开发经验,往往不知道处于开发第一线的用户真正需要什么,无法做到设身处地为程序员着想。缺少使用Go语言的亲身经历,也意味着他们不能在日常开发中,及时发现和改进Go语言的不足。这也是他们往往自我感觉良好的原因。(2016年5月15日补记:2015年8月Go 1.5版本之后不再使用C语言开发。)

    Go语言社区里,有一大批Go语言的推崇者和脑残粉丝,他们满足于现状,不思进取,处处维护心中的“神”,容不得批评意见,不支持对语言的改进要求。当年我对Go语言的很多批评和改进意见,极少得到他们的支持,他们不但不支持还给予打击,我就纳闷了,他们难道不希望Go语言更完善、更优秀吗?我后来才意识到,他们跟乔帮主的苹果脑残粉丝们,言行一脉相承,具有极端宗教倾向,神化主子、打击异己真是不遗余力呀。简简单单的技术问题,就能被他们上升到意识形态之争。现实的例子是蛮多的,有兴趣的到网上去找吧。正是因为他们的存在,导致更多理智、清醒的Go语言用户无法真正融入整个社区。

    如果一个项目、团队、社区,到处充斥着赞美、孤芳自赏、自我满足、不思进取,排斥不同意见,拒绝接纳新方案,我想不到它还有什么前进的动力。逆水行舟,是不进反退的。

    2016年5月15日补记:@netroby:“Golang社区的神经病和固执,我深有体会。我曾经发过Issue,请求Golang官方,能为doc加上高亮,这样浏览器阅读文档的时候,能快速阅读代码参考。但是被各种拒绝. 他们的理由是很多开发者不喜欢高亮。” https://github.com/golang/go/issues/13178

    2016年5月15日补记:C++天才人物、D语言联合创始人Andrei Alexandrescu:“Go所走的路线在一些问题上持有极其强硬和死板态度,这些问题有大有小。在比较大的方面,泛型编程被严格控制,甚至贬低到只有"N"个字;有关泛型编程的讨论都是试图去劝阻任何有意义的尝试,这已经足够让人觉得耻辱。从长远来看,技术问题的政治化是一种极其有害的模式,所以希望Go社区能够找到修正它的方法。”  http://www.csdn.net/article/2015-12-20/2826517

    第3节:还有比Go语言更好的选择吗?

    我始终坚持一个颇有辩证法意味的哲学观点:在更好的替代品出现之前,现有的就是最好的。失望是没有用的,抱怨是没有用的,要么接受,要么逃离。我曾经努力尝试过接受Go语言,失败之后,注定要逃离。发现更好的替代品之后,无疑加速了逃离过程。还有比Go语言更好的替代品吗?当然有。作为一个屌丝程序员,我应该告诉你它是什么,但是我不说。现在还不是时候。我现在不想把这两门编程语言对立起来,引发另一场潜在的语言战争。这不是此文的本意。如果你非要从现有信息中推测它是什么,那完全是你自己的事。如果你原意等,它或许很快会浮出水面,也未可知。

    第4节:写在最后

    我不原意被别人代表,也不愿意代表别人。这篇文章写的是我,一个叫Liigo的80后屌丝程序员,自己的观点。你完全可以主观地认为它是主观的,也完全可以客观地以为它是客观的,无论如何,那是你的观点。

    这篇文字是从记忆里收拾出来的。有些细节虽可考,而不值得考。——我早已逃离,不愿再回到当年的场景。文中涉及的某些细节,可能会因为些许偏差,影响其准确性;也可能会因为缺少出处,影响其客观性。如果有人较真,非要去核实,我相信那些东西应该还在那里。

    Go语言也非上文所述一无是处,它当然有它的优势和特色。读者们判断一件事物,应该是优劣并陈,做综合分析,不能单听我一家负面之言。但是它的那些不爽之处,始终让我不爽,且不能从其优秀处得以完全中和,这是我不得不放弃它的原因。


    关于对作者倾向性质疑的声明

    读者看到本文全都是Go语言负面性的内容,没有涉及一点Go语言好的地方,因而质疑作者的盲目倾向。出现这种结果完全是因为文章主题所限。此前本文末尾也简单提到过,评估一件事物,应当优劣并陈,优势项加分,劣势项减分,做综合评估分析。如果有突出的重大优势,则可以容忍一些较大的劣势;但如果有致命的劣势或多项大劣势,则再大的优势也无法与之中和。中国乒乓球界讲领军人物必须做到“技术全面,特长突出,没有明显弱点”,我甚为赞同。用这句话套用Go语言,可以说“技术不全面(人家自己说成简洁),有一点特长(并发),有明显的弱点(包括但不限于本文列出的这些)”。如此一来,优势都被劣势中和了,劣势还是那么突出,自然是得负分,自然是弃用,自然是没有好印象。我在这里可以说观点鲜明、态度明确,不和稀泥。与其看那些盲目推崇Go语言的人和文章,笼统的说“好”,不如也顺便看看本文,具体到细节地说“不好”。凡是具体到细节的东西,都是容易证实或证伪的,比笼统的东西(无论是"黑"还是"粉")可信性更高一些。

    关于对作者阴谋论的声明

    有某些阴谋论者(例如谢某),说我因一个Pull Request被Go开发者拒绝而“怀恨至今”,暗示此文是故意报复、抹黑Go语言。我对Golang有恨吗?当然是有的,那是一个不爽接一个不爽(如本文一一罗列的那些),逐步累积,由量变形成质变的结果,是我对Golang综合客观评估之后的主观态度,并非由哪一个单独的事件所主导。要说Pull Request被拒绝,Rust开发者拒绝我的PR次数还少吗?比如 https://github.com/mozilla/rust/pull/13014 和 https://github.com/liigo/rust/tree/xp (https://github.com/rust-lang/rust/issues/12842),要是再算上被拒的Issues,那就多的数不清了。我显然不可能因为某些个别的事件,影响到我对某个事物的综合评估(参见前文)。那本文是“故意抹黑”Go语言吗?我觉得不是,理由有二:1、这是作者的主观感受,2、这些感受是以许多客观事实为基础的。如果本文一一列出的那些现象,是不存在的,是虚构出来的,是凭空生成的,那么作者一定是“低级黑”。问题是,那些都是客观存在的事实。把事实说出来,怎么能叫“黑”呢?欢迎读者客观而详细的指正本文中的所有错误。

    关于Golang 2.0的泛型

    今天(2020年2月19日)看到HN的一篇关于 Go2 Generics 的讨论。有网友说到:

    Ken Thompson and Rob Pike are mostly out not at all involved with Go anymore. They are 2/3 of the original creators. It’s different people making the decisions now.

    我找到如下两个链接基本证实了Rob Pike确实已经淡出Golang的核心开发组:

    Rob Pike最近两年仅提交了4k行无关紧要的代码;Ken Thompson就更不用说了,许多年前就已经淡出;Russ Cox似乎也不太参与Go2相关的决策。如此看来,对于Go2的泛型而言,非技术方面的障碍已不存在,剩下只是技术性障碍。

    Ian Lance Taylor 上周(2020年2月13日)说到

    We're working on it. Some things take time.

    展开全文
  • PID:入门到放弃

    万次阅读 多人点赞 2018-12-08 17:34:46
    PID:入门到放弃 前言 ​ 前段时间参加了智能小车的比赛,为了方便和快速性,我们采用了四个麦克纳姆轮结构的小车,并用openmv做视觉导航定位。由于这个项目是第一次做,而且没有学长学姐的指导,不过好歹跌跌撞...

    PID:从入门到放弃

    前言

    ​ 前段时间参加了智能小车的比赛,为了方便和快速性,我们采用了四个麦克纳姆轮结构的小车,并用openmv做视觉导航定位。由于这个项目是第一次做,而且没有学长学姐的指导,不过好歹跌跌撞撞的走过来了。现在,横跨在我们面前的是“如何使小车启动后直走”这一个大魔王。为了战胜它,我们决定去寻找传说中的“pid调节器”圣剑……

    什么是模拟量

    ​ 前情回顾:

    ​ 首先,单片机的一般GPIO口输入输出量为0和1,也就是对应的0V和5V(或3.3V),但是现在的STM32系列的单片机不仅可以输入输出0V和5V,同时也可以输入输出05V之间的电压。但由于05V电压变化范围很小,并且电压值属于一个物理量,这个量并不直观,并且这个量并不能由单片机直接输入输出或处理。我们可以把这个物理量转变为更加直观的数字量,这个数字量不仅可以表现出电压的相对高低,而且这个数字量可以直接给单片机进行数据处理。我们把05这一段区域等分成256等份(这里的256是单片机标准库里所提供的对于模拟量精度的设置,当然用户也可以自己去写一个精度更高的模拟量输出函数),那么05V电压我们就可以用0~255这些整数来表示。这样用一个直观的数字量去表示一个测量量(电压、电流、频率、温度等),这个数字量就是称为模拟量。

    ​ ————by 李杰大佬

    ​ 什么是PWM:

    ​ 我们普通的控制,只需要给其高电平,或者是低电平就可以进行控制了,如果想让其在中间某个值进行控制,就需要使用pwm来控制,也就是快速的对该器件进行开关动作。一个比较常用的pwm控制情景就是,来调节灯的亮度,根据占空比的不同,就可以完成灯亮度的不同控制。pwm的占空比,就是指高电平保持的时间,与该pwm时钟周期时间之比。一般用单片机来产生pwm,有以下几种方法,第一种就是用指令来形成pwm,这种方法可以产生,分辨率很高的pwm,但是这样会影响其他任务的运行,一般使用在比较简单的控制中。另外一种就是用,定时器来产生pwm,还有的单片机本身具有pwm模块功能。

    ​ ————by 度娘

    ​ 怎么测量直流电机的转速:

    电机测速

    什么是PID

    ​ 要了解什么是PID,我们得先弄清楚为什么要PID。众所周知,我们的电机驱动模型在假设输入量确定(为目标转速),输出量确定(PWM一定)时,系统的传递函数模型可以用一个表达式进行表征(这里看不懂的同学可以翻开控制工程看一下),并且该传递函数只和系统本身的特性有关。就好比输入x,传递函数就是y=2x,输出的自然就是2x了。但是,实际情况下输入量和系统都会有一个干扰量,输入电压也会波动,并且输入输出也并不完全成线性关系,这就导致了我们无法实际准确的表达传递函数,这会导致两点后果:第一,哪怕是同一型号规格,两个不同的电机在同PWM输出的时候转速是不同的;第二,同一个电机同PWM在不同路况下会受到干扰的影响,速度也是波动的。

    ​ 这就意味着我们用恒定PWM输出控制小车走直线的愿望破灭了。为了改善速度曲线,经典控制理论习惯使用添加串联校正或者反馈校正,但是刚才提到,我们的实际电机模型是未知的,明确的环节校正是比较困难的。我们联想,在数学中对于一条曲线,在某点附近可以用泰勒展开变成用一个多项式表述一段曲线,而在控制中也是类似,只不过这里的“多项式”变成了PID,即:比例环节,积分环节和微分环节(之所以选择这几个环节的原因参考传递函数的数学表达模型,同样参考控制工程)。

    ​ 在找宝物“pid调节圣器”之前,需要向当地的村民问路是一种常识,百度村的村民们都很热情,提供的资料多种多样,不过因为时间原因,村民们表述不清,个人能力不够等原因,很多资料都是过时的,错误的,甚至无法理解的。作为勇者的我们没有办法,只好拿着钱包抵……抱歉,串台了,只好拿着资料一点点学习对照,终于锁定了若干条有价值的情报:

    1. PID算法讲解

    2. PID控制器

    3. 第八届飞思卡尔智能车北科摄像头一组技术报告

    ​ 经过简单的情报分析,我们对PID有了一个大概的理解,也坚定了我们使用PID调节圣器打倒魔王的决心。那么接下来的就很清晰了:我们现在对每个电机都有一个霍尔传感器,通过这些传感器我们可以测得电机转速,我们假设经过PID调节后四个轮子转速一样,小车不就可以走直线了吗?自以为找到问题关键的我们踏上了寻找PID调节器的不归路。

    ​ 不过,为了能尽快的找到PID圣器,我们偷偷开启了勇者金手指,打算看一下有没有游戏攻略。于是我们特地找到度娘,经过百般恳求,终于发现了arduino单片机居然有现成的,封装好的库!这真是太棒了!老师无数次的告诉我们,不要重复的造轮子,但也一定要有造轮子的能力。为了践行老师的要求,我们特别深入PID库源码,辅助着度娘免费送给我们的这条珍贵的经验:使用PID库,轻松搞定PID(上),终于弄清了事情的真相,跃跃欲试只等动手……

    PID调试和出现的问题

    ​ 很早的时候就听闻PID调试是一个难点,不过这并不是我们放弃的理由。决定采用arduino中封装好的PID库后,问题已经变得简单很多了。观察PID源码,需要我们设定的值有:

    • Setpoint:目标值

    • Kp,Ki,Kd: pid参数

    • OutputLimits:输出极限

    • SampleTime:采样时间

    ​ 一共6个值,Setpoint 相对是最容易理解的,它就是我们想达到的目标值,我们初步设定空载速度为2.00rad/s,尽量慢一点保证摄像头能一次性捕捉到二维码。但是下地跑了一下发现前后运动虽然启动有点困难但是还可以,左右却完全启动不了,排除机械故障和代码问题后,我们认为初始速度过小,左右运动需要速度为4.00rad/s或者以上方可以。

    ​ 接下来是OutputLimits,是输出的极限范围。我们观察代码公式,得知该库是用的位置式PID。关于两者的区别详见:PID控制及位置式与增量式区别,所以我们输出的范围应该是0-255.

    ​ SampleTime的确有点不确定,arduino库例程给的倒立摆的例子是5ms,而一些参考文档则说温度等采样时间是1-5s。

    ​ 我们的第一个问题就是,采样时间如何取呢

    ​ 我们分别设置采样时间5ms,50ms,100ms和200ms设置对照时间,发现采样时间对PID参数的选取有着比较明显的影响,每换一个都要重新调整PID系数。最后,我们参考电机编码器的采样时间,暂时设置成了100ms。

    ​ 接下里就是最复杂的PID调整。一般而言PI控制器就已经能很好的实现速度控制,所以整体调整以PI为主。**如何调整PID呢?**网上关于PID调整的技巧很多,还有口诀什么的,但是对于第一次接触调整的我们来说……恩,这个吗。

    ​ 我们先看一段口诀:

    ​ 参数整定找最佳, 从小到大顺序查。
    ​ 先是比例后积分, 最后再把微分加。
    ​ 曲线振荡很频繁, 比例度盘要放大。
    ​ 曲线漂浮绕大弯, 比例度盘往小扳。
    ​ 曲线偏离回复慢, 积分时间往下降。
    ​ 曲线波动周期长, 积分时间再加长。
    ​ 曲线振荡频率快, 先把微分降下来。
    ​ 动差大来波动慢, 微分时间应加长。
    ​ 理想曲线两个波, 前高后低四比一。
    ​ 一看二调多分析, 调节质量不会低。

    ​ 喵喵喵?什么算震荡频繁?什么叫绕大弯,积分系数我明白,积分时间又是什么鬼?什么叫周期长?动差多少算大,0.05rad/s算吗?还有下面的图,请麻烦告诉我你用什么软件做出来的啊?不知道波形我分析个鬼啊?
    在这里插入图片描述

    ​ 好在天无绝人之路,经过一两天的折磨,大致调整思路差不多整理出来了:

    • 工欲善其事必先利其器,先写一个波形分析程序再说

    • 先将I、D两个系数置为0,只调节P,从1开始,向大调整(如果不震荡),直到P出现较大的震荡为止,然后将P回调一点(70%左右)。调整后可能出现静差,属于正常现象

    • 再调整I,从1开始逐渐放大(如果不震荡),观察,当I较小的时候并且存在静态误差时候,会经过很长很长时间速度才会逐渐到达标准值,增大I会减小反应时间,而如果I过大,则会产生明显的超调现象。我们需要在不改变P的情况将I调整到不出现震荡,然后再回调一点。

    • 同时放大P和I到尽量大,同时保证图像不震荡。

    • 如果和震荡周期有关,则调整P,如果和超调及反应速度有关,则调整I(I放大好像就把波拉长了一样)

    • 如果震荡过于频繁,则可少量的加入D使图像稍微平滑一点,但是尽量不要加。微分环节在传递函数中表示超前量,及对当前运动趋势(求导,斜率)进行估计从而提前反应,

      ​ 单一串口波形分析python程序如下,用matplotlib和numpy库:

      import matplotlib.pyplot as plt
      import numpy as np
       
      setpoint = 20 
      items = []
      fr = open('data.txt','r')
      points = fr.readlines()
      for point in points:
          # print(point)
          items.append(float(point))
          
      length = len(items)
       
      minX = 0
      maxX = len(items)*100
      X = np.arange(minX,maxX,100)
      
      # 图像中显示
      plt.plot(X, items, color = 'red')
      plt.plot(X, items,'ro', color = 'blue')
      plt.xlabel('Time (s)')
      plt.ylabel('n (rad/s)')
      plt.show
      

      ​ 但是!这绝对不是结束,而才刚刚开始……

      PID优化与选择

      ​ 观察整个速度曲线感觉仍然不是很满意,思考一下,问题出在那里呢?

      猜想:是不是启动时候获得的pwm太大,以至于不容易下来出现超调呢?

      实验:如果利用两个轮子的速度差作为输入会不会好一点呢?

      过程:问题出现在了最后输出上,现在我们将两个电机作为一个大的系统,怎么处理最后得到的Output呢?

      方案:输出分别为 pwm[i]+Output 和 pwm[i]-Output

      结果:四个轮子极难调节,放弃该方案

      猜想:启动时,PWM调整初始值为目标值附近会不会提高快速性?

      结果:影响不大。

      猜想:初始值放大,能提高快速性吗?

      结果:影响不大,第一个Output很快会改变初始PWM

      猜想:输入值大小会不会对波动产生影响?比如输入2.00和20.00会有影响吗?

      实验:分别将输入和目标值同时放大1、10、100倍

      结果:PID发生改变,但是性能没有优化太多。

      ​ 到这里我已经连续建立了3个不同的PID模型了,但是连续失败让我突然有了一点疑惑,难道位置PID不适合速度调节吗?我决定先以当前最优的模型来跑一下,于是又突然发现了一个严重的问题:

      ​ **四个轮子从开始到到达目标值附近的波动速率是不一致的。**即,小车在刚开始会出现转向,到最后才会走直线。这完全不符合比赛的要求,我希望它在启动后能直接走直线。这个发现促使我放弃了位置式PID,因为在实际调试中我发现位置式PID的波动很大,我希望能减小这种波动,所以我更换了增量式PID。模仿PID库,我重写了PID类,将其改成了增量式,以后的工作都是在增量式调节的背景下进行的。

      ​ 增量式与位置式的一个不同在于,它是有初始值的。同时,为了解决启动不走直线的问题,我们决定放大初始值的作用,设置一个滤波器,当当前速度与目标速度相差大于常量error后,PID才真正启动。初始值可以用开环调试的方法测出来,保证启动时尽量沿直线启动。而error呢……慢慢试吧。需要调整的参数不过从6个上升为8个而已,没什么大不了的,没什么大……哇的一声哭了出来。

      ​ 或许因为调整的次数比较多,熟练了,增量式的调整明显快了起来。最后1轮的速度曲线可以看出来还不错:

    在这里插入图片描述

    ​ 接下来四个轮子:
    在这里插入图片描述

    ​ 总之,差不多了,可以下地跑一跑了。

    ​ !!!

    ​ 发生了什么?为什么它还是跑歪!

    ​ 仔细复盘中…………………………

    猜想:霍尔传感器的精度不够

    实验:恒定PWM=100输出,输出转速曲线

    结果:
    在这里插入图片描述

    改进:加入滤波器,并且采用 (double)( (int)转速 ) 的方法降低精度

    效果:好了一些,但是还是不行

    猜想:是不是因为没有悬挂底盘导致车轮没法完全接触地面导致打滑之类的现象

    实验:将小车放入柔软的地面实验

    结果:不是这个原因

    猜想:传感器精度、安装误差、摩擦角非完全45度导致的累积误差

    实验:没法实验

    结果:………………

    ​ 这时候我又看到了放在前面的北京科技大学飞思卡尔智能车摄像头一组的技术报告,发现他们同时使用了速度PID和……方向PID!我们的目标不是控制方向吗?既然速度不行,那么方向可不可以呢?

    猜想:用方向PID取代速度PID

    实验:仔细研究他们的方向PID,发现一个很绝望的事情,他们的方向控制完全是由转向一个舵机控制的,而麦克纳姆轮呢……抱歉打扰了;并且,方向PID的输入将不会是转速,可以是视觉,也可以是陀螺仪。前者由于由openmv单独处理,和主控通讯进行调控代价太大,而后者……不会,需要学习,临近考研的学习代价更大。

    结果:放弃PID

    结论与未验证猜想

    ​ 咳咳,英雄不问出处,既然没有或者PID调节器这个圣器,但是我们还有开环控制这个板砖能用吗!不是说板砖在手,天下我有吗!用开环控制战胜“走直线大魔王“也是可以的吗!

    ​ 所以,总结一下,历时3个星期的学习和调试,进度为零,经过了从入门、学习、钻研、到放弃的全过程,呼应题目,非常完美。

    ​ 其实我私以为PID应该还是可以继续调整的,但是时间的确不多了,所以无奈之下选择放弃,到最后,仍然有几个猜想没有试验,留待小学弟们解决了:

    1. 减速比大的电机是不是转速输出更加平稳
    2. 目标速度对速度曲线性能的影响

    结语

    ​ 这可能是我,还有我的小伙伴们,郑鸿和李杰两位大佬们在大学期间参加的最后一场比赛了吧,这次比赛之后我们即将准备考研,然后各奔西东。我们大胆地选择了一个没有人做过的项目,试着挑战一下极限,作为自己的大学里青春的结束。谢谢两位愿意陪我尝试,即使花了大把时间也极有可能拿不到奖项的比赛,也谢谢几位老师的指导和帮助。不管怎么样,祝我们成功吧!

    后记

    现在再来看这篇文章,真是简单和天真的。简单的讲一下我现在的看法:我们当时做的搬运小车使用的是减速电机, 而减速电机需要PID进行调节的,使用它的原因不在于控制方向,而是平稳速度。因为减速电机不像步进电机一样速度是可控的,它的速度是有上升和波动的,PID的目的在于平稳速度曲线和降低上升的反应时间,不需要同步,甚至说每个轮子的目标PWM都是不一样的才对(小车设计的负载不一样,每个轮子在负载不同的情况下要跑出相同的速度必须要不同的PWM),方向的控制可以用陀螺仪,或者像我之前一样用视觉,甚至可以直接盲走。这个和飞思卡尔方向PID不一样,飞思卡尔智能车转向通过舵机控制,所以可以做方向PID,而对于麦轮驱动的搬运小车而言,其实没有必要,速度PID足够了。
    其实比赛的时候更多的人用的是步进电机。步进电机有它的优点,它的速度比较容易控制,但是相比减速电机,它的驱动负载能力相对较弱。如果你的车子又大又重,还是建议减速电机比较合适。减速电机记得一定要选择一个合适的减速比,在速度和稳定性方向做一个平衡。

    展开全文
  • 《Java入门到放弃》文章目录

    千次阅读 多人点赞 2019-03-12 10:52:05
    《Java入门到放弃》文章目录(红色的表示还编写) 《Java入门到放弃》JavaSE入门篇:变量 《Java入门到放弃》JavaSE入门篇:运算符 《Java入门到放弃》JavaSE入门篇:程序结构 《Java...

    《Java从入门到放弃》文章目录(红色的表示还没编写)

     

    1. 《Java从入门到放弃》JavaSE入门篇:变量

    2. 《Java从入门到放弃》JavaSE入门篇:运算符

    3. 《Java从入门到放弃》JavaSE入门篇:程序结构

    4. 《Java从入门到放弃》JavaSE入门篇:数组

    5. 《Java从入门到放弃》JavaSE入门篇:练习——单身狗租赁系统(数组版)

    6. 《Java从入门到放弃》JavaSE入门篇:面向对象概念(入门版)

    7. 《Java从入门到放弃》JavaSE入门篇:面向对象语法一(入门版)

    8. 《Java从入门到放弃》JavaSE入门篇:面向对象语法二(入门版)

    9. 《Java从入门到放弃》JavaSE入门篇:集合

    10. 《Java从入门到放弃》JavaSE入门篇:异常

    11. 《Java从入门到放弃》JavaSE入门篇:单元测试

    12. 《Java从入门到放弃》JavaSE入门篇:文件操作

    13. 《Java从入门到放弃》JavaSE入门篇:JDBC(入门版)

    14. 《Java从入门到放弃》JavaSE入门篇:练习——单身狗租赁系统

    15. 《Java从入门到放弃》JavaSE入门篇:网络编程(入门版)

    16. 《Java从入门到放弃》JavaSE入门篇:多线程(入门版)

    17. 《Java从入门到放弃》JSP入门篇:数据交互

    18. 《Java从入门到放弃》JSP入门篇:状态管理

    19. 《Java从入门到放弃》JSP入门篇:EL和JSTL

    20. 《Java从入门到放弃》JSP入门篇:Serlvet入门

    21. 《Java从入门到放弃》JSP入门篇:Servlet过滤器和监听器

    22. 《Java从入门到放弃》JSP入门篇:XMLHttpRequest的基本用法

    23. 《Java从入门到放弃》框架入门篇:Struts2的基本访问方

    24. 《Java从入门到放弃》框架入门篇:Struts2的基本访问方式(二)

    25. 《Java从入门到放弃》框架入门篇:Struts2的基本数据传递方式

    26. 《Java从入门到放弃》框架入门篇:Struts2的常用基本标签

    27. 《Java从入门到放弃》框架入门篇:Struts2的常用验证方式

    28. 《Java从入门到放弃》框架入门篇:Struts2的常用验证方式(二)

    29. 《Java从入门到放弃》框架入门篇:Struts2的拦截器基本语法

    30. 《Java从入门到放弃》框架入门篇:spring中IOC的注入姿势

    31. 《Java从入门到放弃》框架入门篇:spring中AOP的配置方式

    32. 《Java从入门到放弃》框架入门篇:springMVC基本用法

    33. 《Java从入门到放弃》框架入门篇:springMVC数据传递

    34. 《Java从入门到放弃》框架入门篇:springMVC数据传递 (二)

    35. 《Java从入门到放弃》框架入门篇:springMVC数据校验

    36. 《Java从入门到放弃》框架入门篇:hibernate基本配置

    37. 《Java从入门到放弃》框架入门篇:hibernate基本用法

    38. 《Java从入门到放弃》框架入门篇:hibernate中的多表对应关系

    39. 《Java从入门到放弃》框架入门篇:hibernate中的多表对应关系(二)

    40. 《Java从入门到放弃》框架入门篇:使用注解的方式配置hibernate映射关系

    41. 《Java从入门到放弃》框架入门篇:hibernate查询——HQL

    42. 《Java从入门到放弃》框架入门篇:在MyEclipse中配置Mybatis框架

    43. 《Java从入门到放弃》框架入门篇:在MyEclipse中配置Maven

    44. 《Java从入门到放弃》框架入门篇:在MyEclipse中配置Maven的注意事项

    45. 《Java从入门到放弃》框架入门篇:在MyEclipse中创建SpringBoot项目

    46. 《Java从入门到放弃》框架入门篇:SpringBoot+mybatis搭建三层架构项目

    47. 《Java从入门到放弃》框架入门篇:SpringBoot+mybatis使用注解方式实现mapper

    48. 《Java从入门到放弃》框架入门篇:在SpringBoot中使用thymeleaf模板

    49. 《Java从入门到放弃》框架入门篇:使用kaptcha验证码框架

    如果大家有什么感兴趣的入门级的内容,可以在评论回复。
     

    展开全文
  • ”“这些岗位以后被替代”“金融业降薪潮到来”为啥 因为焦虑可以带来流量好吧 作为一个程序员 恭喜 你们成功的使我焦虑了既然你们让我35就人要 那么 我决定 还是35降低到25吧因为我很喜欢一句话 “走别人的路...


    第一次在CSDN发表文章如果写的不好请各位多多包涵

    最近很多无良媒体开始炒作焦虑

    “35的程序员没人要 ”

    “这些岗位以后被替代”

    “金融业降薪潮到来”

    为啥 因为焦虑可以带来流量

    好吧 作为一个程序员 恭喜 你们成功的使我焦虑了

    既然你们想让我35就没人要 那么 我决定 还是从35降低到25吧

    因为我很喜欢一句话 “走别人的路 让别人无路可走”

    既然你们这些年轻人这么想快点上位 赶走我们这些老家伙

    那么我觉得 也不要让你们好过比较好

    我决定写一套教材出来 从各个角度维度 全方位的介绍这个行业

    让各行各业  或者马上毕业 非计算机专业又想入软件行业的

    都可以快速的入门软件行业 让这个行业的基数变得更大一点不是更好玩么

    我的目标就是 哪天买菜阿姨想转行了 抄起电脑就是一句 hello world 你怕不怕

    搬砖大叔累了抄起电脑就是一句for循环 你怕不怕

    人人都看得懂的教程才是好教程

    来呀 互相伤害呀


    我专门浏览了很多网上的教程 总结了以下几个套路

    1 先一顿昏天黑地猛夸这个行业怎么怎么好 工资怎么怎么高 然后一看就是 收费不要998只要688

    2 免费的教程一上来就是变量啊 对象啊 一顿操作猛如虎 观众一脸懵逼

    3 再就是无章无序的东一点西一点 不成体系 看着java教程倒是把失眠给治好了

    所以我的文章首先会注意的是 门槛 门槛一定要在地平线底下

    如果有悟性高的小盆友 说不定你看完我的教程就可以去软件公司面试咯~

    然后就是倾囊相授 我会毫无保留的交出我的所有

    我会的我全教 我不会的 我学会了再教

    我没教的你告诉我 我马上来教 总之 保证你们都有收获

    再就是 我们的目的是进入软件公司 而不是教你成为大牛 毕竟我不是大牛

    我带你入门 修行靠个人

    所以很多深入的东西可能不会讲解 但是学完教程直接上岗是没有问题的


    这个行业有太多的人想给他保留神秘感

    所以导致了很多人一听到IT啊 软件啊 就觉得深不可测 敬而远之

    不过不好意思 我会把这些神秘的面纱统统揭露下来

    让大家想转行过来的感觉到毫无压力

    “哟 原来是这么回事啊 我当是啥高科技 原来就是搬个砖”

    相信我 你们继续看我的文章会有这种感觉的


    再说一下门槛的问题 可能写代码的时候需要用到英文

    很多人就怂了 千万别怂 我告诉你 从头到尾

    你死记硬背只需要记住不到30个单词

    而且每个单词长度不超过10个字母

    其他的需要用的时候就百度有道查词典就行了


    下次 给大家带来点我的故事吧 我也是转行来干程序员的

    或许你们听了我的故事 会更有动力也说不定

    展开全文
  • 当我们放弃时,我们些什么

    千次阅读 2014-01-04 00:26:28
    我所放弃之多,放弃时所经历的痛苦,与一般无异。 最终,我也能学会打三角洲部队这个游戏。当年大哥连忽悠带嘲笑让我迷上这个游戏,但是我是切切实实地吐了几顿。听到机箱风扇的声音都受不了,连多几秒钟...
  • 1、LabVIEW入门到放弃

    千次阅读 2021-03-14 22:22:50
    LabVIEW入门到放弃一、其实不学二、学习资源三、成果展示四、声明 一、其实不学   最近导师要用LabVIEW写点东西,进行一些实验验证。虽然之前摸几天,也就是为了应付考试,啥都学到。接下来时间可能真...
  • 说实在目前遇到的问题,已经让我放弃自己开发富文本了,真的是非诚勿扰。这个坑那么简单。(捂脸哭) 开发环境:vueCli3.X 一、HTML5的富文本 contentEditable="true" 这个属性我不介绍了,不懂得自己百度 ...
  • FPGA视觉入门到放弃——Canny算子

    千次阅读 2016-04-21 21:37:08
    FPGA视觉入门到放弃——Canny算子 一. FPGA视觉入门到放弃简介 本笔记仅适合实验室内部的FPGA图像采集卡,并不适合计算机视觉中的高大上场合,但功耗和速度很阔怕,同时方法简单得阔怕。毕竟没有什么方法...
  • 如果在追梦的路上感觉到累了,放弃的时候。请看看我的这篇文章
  • GO:搭建到放弃

    千次阅读 2018-03-10 10:57:26
    从没想过搭建一个环境会那么吃力。原因就是想装X用了vscode,看看这界面,多帅,,然后就碰到了各种各样的问题(翻越gfw,需要先装git等等等等)。不忍了,还是用ide,傻瓜式操作,ez。推荐一下 go语言圣经:点我。...
  • vim入门到放弃

    万次阅读 多人点赞 2016-11-02 20:00:51
    vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的。下面的文章翻译自《Learn ...你想以最快的速度学习人类
  • 你想过自己注定是一个普通人吗?

    千次阅读 多人点赞 2020-04-06 17:42:56
    "你想过自己注定是一个普通人吗?" "农业革命让人能够开创出拥挤的城市、强大的帝国,接着人类就开始幻想出关于伟大的神灵、祖国、有限公司的故事。虽然人类的基因演化仍然一如既往慢如蜗牛,但人类的想象力却是极速...
  • markdown入门到放弃

    千次阅读 2016-11-12 22:55:18
    Markdown入门到放弃第一次上手typora真是令人愉快,看着都舒坦。markdown兼容html听起来是不是极具诱惑力,但这并不重要,重要的是markdown的生成器足够智能,不会在HTML区块标签外加上多余的。而且code时,不论是...
  • 这是发生在我身上的一个故事:一个零起点学习编程的小白,到成为Udacity的一名全栈工程师。(Udacity是一家致力于编程在线教育的创业公司)。 在找第一份工作之前,几乎每个招聘初级网络工程师的帖子都要求...
  • 心理小测验“有五种动物,听好了:老虎、猴子、孔雀、大象和狗,到一个从未的原始森林里探险,带着这五种动物,一路上险境重重,不可能将它们带到最后,不得不将它们一一放弃会按什么样的顺序放弃呢?...
  • 【微信小程序】入门到放弃

    万次阅读 2016-10-25 21:40:52
    关于微信小程序是什么,能做什么的问题,草民在此不在罗列了,随着小程序的天天刷屏,想必您也是来吃一些干货,本篇博文和大家走进微信小程序的入门到放弃~
  • 软件工程师的自白:真希望自己从没编码

    万次阅读 多人点赞 2020-12-01 18:27:37
    尽管从事这一领域会有回报,但我必须承认:有时真希望自己从没编码。 怪癖 自从我开始学习编写代码以来,浏览网页就变得不一样了。我再也不能忍受我在使用其他网站时遇到的错误。每当有什么奇怪的事情...
  • 自从Spring发行4.x后,很久去好好看Spring源码了,加上最近半年工作都是偏管理和参与设计为主,在技术细节上或多或少有点疏忽,最近心血来潮重新下Spring源码,对以往Spring知识点做下回顾,但是在构建环境的...
  • 为了避免使用两套代码,办法,调研到最后,bgfx满足, 且大神持续开发,国内的牛人“云风”也在更新,但是这玩意资料太少太少了, 而且我们用过程中遇到有些很挫的bug,但是好多已经改了。入坑需谨慎。
  • 放弃我是一生的错,现在也许感到。但是总有一天会后悔! 真是个大混蛋,我现在非常非常的恨,恨不得冲到家去杀! 非要真让我生气,骂你没良心才开心吗?真的不是普通的贱货! 他XX的,我这辈子...
  • Java 入门到放弃

    万次阅读 多人点赞 2016-11-12 13:58:43
    主要是介绍一下我JAVA零基础到工作四个月之间这一段事情。后面会陆续写一些工作之中用到的一些东西和记录一些自己学习相关技术的文章。目的是为能够跟别人一起多交流分享学习经验,共同提高能力。如果能帮到一两...
  • 一个 jQuery 插件,适用于从未听说 id 属性的糟糕 Web 开发人员。 如果您真的使用此代码,那么您是一个可怕的人,可能应该放弃编程并从事山羊养殖。 这个插件是在遇到生产中运行的七级嵌套 parent() 调用后创建...
  • jQuery mobile入门到放弃

    万次阅读 2018-01-31 01:43:17
    今天做移动端项目的时候了解了下它的事件,简单实用,触摸滑动基本都有,因为之前小组默认都是jQuery来写代码来着,所以官网下载出来准备直接开动,但一直到中午到晚上,他让我感觉到越来越绝望,现在,我把我...
  • 25岁小吗,即使你没成家立业,但是,真的不小了。  25岁的时候不多想想,等到30岁后再去吗。  25岁不开始慢慢走出来,非要等到30随后吗。  最近大家都说35岁中年危机,说的20多岁很成功...
  • 10分钟JAVA入门到放弃

    千次阅读 2016-12-14 20:28:34
    10分钟JAVA入门到放弃author:AIDreamerblog:http://blog.csdn.net/mmy1996last modified on :2016/12/12面向对象的三大特征⎧⎩⎨1封装:将数据和操作封装到类中2继承:子类可以继承父类的属性和功能3多态:操作...
  • Android Room入门到放弃

    万次阅读 2017-12-07 17:15:02
    2. 非基于运行时注解,仅对SQLite API进行简单封装,效率问题 3. 存储对象里嵌套对象时,可使用@Embedded注解进行自动拆分存储。Room会把被嵌套对象里的字段放置存储对象对应的表里,例如上例,Student表里不仅...
  • JavaEE 入门到放弃(四):MVC 模式

    万次阅读 多人点赞 2019-01-06 18:43:42
    ,一开始我觉得什么毛病,字面上完全说地通,但是仔细一, “设计模式” 这个词是一个术语,是指工厂模式、单例模式这些东西,是不能乱用的。而 MVC,应该被称作是 “框架模式” 。 那么两者有什么区别呢...
  • 程序猿入门到放弃

    千次阅读 2017-04-11 10:35:27
    如果在大城市混了几年,回老家个没有喧嚣的安逸的生活(大城市并不是不安逸),找个轻松的工作或做些小生意。那也未尝不可。买两个大房子,结个婚(不结婚,妈妈会揍的),幸福的生活着。 会计从业资格证...
  • Servlet了解到放弃(07)

    万次阅读 2020-06-29 14:26:45
    在HomeServlet中获取传递过来的tagId,此时HomeServlet有两种功能了, 这两种功能就由tagId是否有值来区分,值就查询所有,有值就查询标签相关作品 调用dao中的findByTagId()方法 实现dao中的findByTagId方法,和find...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 239,489
精华内容 95,795
关键字:

从没想过放弃你