您的位置:网站首页 > Java工具 > 正文

为什么我这个 Java 死忠倒向了 Nodejs?

类别:Java工具 日期:2018-9-7 16:50:33 人气: 来源:

  【CSDN编者按】Java作为编程语言的老大,其功能性和适用性自不必多言,常年雄踞各大榜单榜首。本文的作者却有不同的看法。作为一个拥有十多年Java工作经验的老程序员,却无情地倒戈了,扑向了

  作为一个在Sun微系统公司Java SE团队工作了十多年的人,难道不应该是体内流淌着Java字节码的血、只要一息尚存就要不断实现抽象接口吗?但对于我这个前Java SE团队来说,2011年学习了Node.js平台后就像是呼吸到了新鲜空气一样——我在2009年1月被Sun裁退之后(正好在Oracle收购之前),开始学习Node.js并被它深深所吸引。

  我是怎样被吸引的?从2010年起,我就开始写各种关于Node.js编程的东西了。具体来说,写了四版《Node.js Web开发》,加上一些其他的书,和数不清的关于Node.js编程的教程。可以说,我花了非常多的时间解释Node.js和Java语言的发展。

  在Sun微系统工作时,我相信一切都能用Java描述。我在JavaONE上,与别人共同开发了java.awt.Robot类,举办了Mustang Regressions Contest(为Java 1.6发布准备的找bug竞赛),帮助别人启动了“Java的分布式授权”,就是在OpenJDK出现之前为Linux发行版发布JDK的解决方案,后来还在启动OpenJDK项目上扮演了一个小角色。

  一走来,我在有过一个博客(现在荒废了),连续六年每周写一到两篇文章,讨论Java生态系统中发生的一切。最常见的话题就是反驳那些唱衰Java的论调。

  那么,说好的靠着Java字节码和呼吸呢?我这篇文章的目的就是想解释下一个Java字节码的粉丝是如何变成了Node.js/Java的者。

  过去三年里我也写了许多Java/Spring/Hibernate代码。虽然我很喜欢我的工作——我在Solar Industry工作,做一些实现梦想的事情,如写数据库查询语句查询用电量等,但用Java编程已经是昨日黄花了。

  Spring的编程体验非常好,但是一旦在子系统深处出现模糊难懂、从未见过的异常信息,就需要花掉三天以上才能找出问题是什么。

  Node.js是一个人磨砺并精炼轻量级事件驱动架构的结果,直到Node.js自己揭露了。

  Java缺少Java那种严格类型检查,但这是个双刃剑。编程变得容易了许多,但需要更多测试才能正确。

  Java和Node.js都提供优秀的性能,这与“Java很慢因此Node.js的性能必然不好”的传说正相反。

  一些工具或对象是设计师多年精心磨砺并提炼的结果。他们会尝试不同的想法,去掉不需要的特性,最后得到为某个目的量身打造的对象。因此这些对象都有强大的简单性,所以非常吸引人。而Java并不是这种系统。

  Spring是个流行的Java Web应用程序开发框架。Spring,特别是Spring Boot,其核心目标是个预配置的、易用的Java EE栈。Spring程序员不需要考虑所有servlets、数据持久、应用服务器,以及构成系统的其他不知所云的东西。Spring会处理这一切,而你只需要专注写代码即可。例如,JPA Repository类会将数据库查询合成为方法,名字类似于“findUserByFirstName”,这样你无需写任何代码,只需要调用方法,Spring就会处理剩余的一切。

  如果你得到一个Hibernate PersistentObjectException,提示“ detached entity passed to persist”,也就是说到达REST访问点的JSON有ID值,而这层含义往往需要几天时间才能理解。这就是过度简化的代价。Hibernate也在过度简化,它希望控制ID值,于是抛出了这个不知所云的异常。在Spring的栈中,子系统一个接一个,它就像复仇一样耐心地等待你犯哪怕最微小的错误,然后用应用程序崩溃的异常来惩罚你。

  紧接着你就会看到巨大的栈信息。它们有好几个屏幕那么长,充满了一个又一个抽象方法。Spring显然做了许多工作来实现代码的功能。这种级别的抽象显然需要大量的逻辑,来找出所有信息并执行请求。长长的栈不一定是坏事,它指出了一个症状:这需要多少内存和性能上的额外开销?

  既然程序员不需要写任何代码,那么调用“findUserByFirstName”时,它是怎么执行的?框架需要解析方法名、猜测程序员的意图、构建类似于抽象语法树的东西、生成SQL等等。这些事情的额外开销有多大?一切都只为了让程序员不需要写代码?

  在经历了多次,浪费了许多天的时间学习本来不需要学习的东西后,你也许会产生与我同样的:复杂性不会产生简单性,只会让系统更复杂。

  “兼容性很重要”是句很好的口号,它的意思是Java平台的主要价值体现在它完全后向兼容。我们很看中这一点,连T恤衫上都印了这句口号。当然,维持这种程度的兼容性非常痛苦,有时候还可以用来避免那些已经没用的老方法。

  首先是Ryan Dahl在开发Node.js平台核心时采用的设计美学。Dahl的经验是,线程会导致重量级的复杂系统。他想找一些不同的东西,花了很长时间打磨并提炼了一系列核心思想放到了Node.js里。结果就是一个轻量级、单线程的系统,巧妙地利用了Java的匿名函数进行异步回调,还有一个巧妙的运行时库用来实现异步。最初的基调是通过回调函数的事件发布来实现高吞吐量的事件处理。

  然后就是Java语言本身了。Java程序员似乎很喜欢移除样板代码,使得程序员可以专注于有用的事情。

  另一个用来对比Java和Java的例子就是事件处理函数的实现。在Java中,事件处理函数需要创建一个实际的抽象接口类。这就需要许多冗长的代码,使得代码本身的意图含混不清。程序员的意图埋在那一大堆样板代码后面,谁能看得清呢?

  而在Java中,你只需要简单地使用匿名函数,也就是闭包。不需要搜索正确的抽象接口,只需要写下必须的代码,没有任何冗余。于是有了另一个教训:大多数编程语言只会程序员的意图,使得代码更难理解。

  Java的异步编程一直有两个问题。一个就是Node.js中所谓的“回调陷阱”。很容易就陷入嵌套回调函数的陷阱中,每层嵌套都会让代码更复杂,使得错误处理和结果处理更困难。一个相关的问题就是Java语言不会帮助程序员恰当地表达异步执行。

  这段代码实现了Unix的cat命令。async库用来简化异步执行序列很不错,但它用了许多样板代码,混淆了程序员的真实意图。

  我们实际想写的是个循环。但不能写成循环,而且也不是自然的循环结构。进一步,错误处理和结果处理不在最自然的地方,而是违反常规地写到了回调函数内。在Node.js支持ES2015/2016之前,这是人们能做到的最好方法。

  这段代码用async/await函数重写了前面的例子。还是同样的异步结构,但使用了正常的循环结构来书写。错误和结果处理的也很自然,代码更易于理解,更容易编写,而且也可以很容易地理解程序员的意图。

  回调陷阱并不是用复杂性的方式解决的。相反,语言和范式的改变解决了回调陷阱的问题,同时还解决了过多样板代码的问题。有了async函数,代码就更漂亮了。

  尽管最初这是Node.js的缺点,但优美的解决方案将缺点变成了Node.js和Java的优点。

  我之所以是Java的死忠,原因之一就是严格的类型检查使得Java可以用于编写巨型应用。当时的风向是编写宏系统(没有什么微服务、Docker之类的),由于Java有严格的类型检查,Java编译器可以帮你避免许多类型的bug,因为不好的代码无法通过编译。

  相反,Java的类型很松散。理论也很明显:程序员无法确定他们收到的对象的类型,那他们怎么知道该做什么呢?

  Java的强类型的缺点就是太多样板代码。程序员要不断进行类型转换,否则就得努力一切都分毫不差。程序员要花掉很多时间写极其精确的代码,使用更多的样板代码,以图早期发现错误并改正。

  这个问题十分严重,因此人们必须使用大型、复杂的IDE。简单的编辑器是不够的。让Java程序员保持正常的唯一方式就是提供下拉菜单供他选择对象中可用的字段,描述方法的参数,帮助他创建类,协助他做重构,以及其他一切Eclipse、NetBeans和IntelliJ能提供的功能。

  在Java中,许多类型不需要定义,通常也不需要用类型转换。因此代码更清晰易读,但存在漏掉编码错误的风险。

  在这一点上Java是好是坏取决于你的观点。我十年前认为,用这些额外开销获得更多确定性是值得的。但我现在认为,我靠怎么要写这么多代码,还是Java简单。

  Node.js鼓励程序员将程序分割成小单元,即模块。看似是一件小事,但却部分地解决了刚才提到的问题。

  人们对JavaScripot的恐惧一般集中在它缺乏严格的类型检查,因此代码很容易出错。对于小型、目的明确并且有着清晰边界的模块来说,受影响的范围通常会在模块内部。因此大多数情况下需要考虑的范围很小,而且都安全地在模块的边部。

  通过书写简单的Java代码而节省下的时间,必须花一部分在增加测试上。测试用例必须捕获那些本应被编译器捕获的错误。你肯定会测试代码的,对吧?

  如果想在Java中享受静态类型检查,周冰 季建业可以试试Type。我没用过Type,但听说它很不错。它增加了包括类型检查在内的许多有用的功能,并且可以直接编译成兼容的Java。

  Maven想想就觉得,完全不知道该写什么。我觉得,肯定有人非常喜欢Maven,也肯定有人很讨厌Maven,两者之间没有中间地带。

  Java生态的问题之一就是它没有统一的包管理系统。Maven包还算可以,而且理论上应该能在Gradle中使用。但不论是用途、易用性还是功能上,Maven与Node.js的包管理系统相比简直是天壤之别。

  在Node.js的世界里有两个非常优秀的包管理系统,他们能合作得很好。最初只有npm和npm的代码仓库。

  npm用一种非常好的格式描述包的依赖关系。依赖可以是严格的(精确的版本1.2.3),或者可以逐渐增加较松散的条件,直到使用“*”表示任何最新版本。Node.js社区已经向npm代码仓库发布了几十万个包。在npm代码仓库之外使用这些包也同样容易。

  最好的地方是npm代码库不仅供Node.js使用,也可以让前端工程师使用。以前他们使用类似于Bower之类的包管理工具。现在,Bower已经过时,所有的前端Java库都以npm包的形式存在。许多前端工具链如Vue.js CLI和Webpack都是用Node.js编写的。

  Node.js的另一个包管理器yarn从npm代码仓库下载包,而且使用与npm相同的配置文件。yarn与npm相比的主要优势就是运行得更快。

  在创建了java.awt.Robot之后,我想出了这张图。的Duke吉祥物完全由曲线组成,而RoboDuke则都是直线,除了肘关节处的齿轮之外。

  两者都由编译器将源代码转换成字节码,再由虚拟机执行。VM通常会将字节码再次编译成原生代码,并使用各种优化技术。

  Java和Java在性能方面都有巨大的需求。Java和Node.js需要快速的服务器端代码,在浏览器中的Java则需要更好的客户端应用性能。

  Sun/Oracle JDK使用HotSpot这个超级虚拟机,它采用了多字节编译策略。它的名字表示,它会检测经常执行的代码,一段代码执行次数越多,就会应用越多的优化。因此HotSopt可以产生非常快的代码。

  而对于Java,我们曾一度:谁能期待浏览器中运行的Java能实现任何复杂应用程序呢?办公文档套件肯定没办法用Java在浏览器中实现吧?但今日,这一切都实现了。本文就是用Google Docs写的,它的性能还不错,浏览器上运行的Java性能每年都有大幅度增长。

  机器学习领域涉及到大量数学计算,因此数据科学家通常使用R或Python。包括机器学习在内的几个领域都需要快速数值计算。许多原因导致Java很不擅长数值计算,但人们已经在努力开发一个标准库,使得Java也可以进行数值计算。

  Java还可以使用Tensorflow中的一个新的库:TensorFlow.js。它的API类似于Python的TensorFlow,可以导入训练好的模型,用来做比如分析动态视频以识别训练过的物体等工作,而且可以完全在浏览器中运行。

  此前IBM的Chris Bailey在介绍Node.js的性能和扩展性问题时,就介绍了关于Docker/Kubernetes部署方面的问题。他从一系列性能评测开始谈起,证明Node.js在I/O吞吐量、应用程序启动时间和内存足迹方面的性能已远远超过了Spring Boot。而且,由于V8引擎的改进,Node.js的每次发布都会带来巨大的性能提升。

  Bailey还表示,人们不应该在Node.js运行计算类的代码。理解其原因非常重要。因为Node.js是单线程模型,长时间运行的计算会阻塞事件的执行。在我的《Node.js Web开发》一书中,我谈到了这个问题,并介绍了三种方法:

  如果Java的进步还达不到应用程序的要求,那么还有两种方法可以直接在Node.js中集成原生代码。Node.js的工具链包括node-gyp,它能处理链接原生代码模块的工作。WebAssembly能将其他语言编译成一个执行速度很快的Java子集。WebAssembly是一种可执行代码的便携式格式,可以在Java引擎中运行。

  十年前软件行业谈论的话题就是,用快速的Java引擎运行富互联网应用,从而使得桌面应用失去存在的必要。

  实际上,这件事情20年前就开始了。Sun和Netscape达成了一项协议,在Netscape浏览器中运行Java Applets。当时Java语言是作为编写Java Applets的脚本语言的一部分出现的。当时的希望是在服务器端运行Java Servlets,在客户端运行Java Applets,从而达到前后端使用同一种语言的目的。但由于许多原因,这个目标并没有实现。

  十年前,Java开始变得足够强大,可以用来实现复杂的应用程序了。因此出现了RIA这个词,而且RIA据称将在客户端应用的平台上干掉Java。

  今天我们可以看到,RIA的想法已经实现了。通过服务器端的Node.js,我们终于可以实现了当年的目标,但两侧的语言却都是Java。

  Java在桌面应用平台上失败的原因并不是Java的RIA,主要原因是Sun微系统对于客户端技术的。Sun专注于要求快速服务器端性能的企业客户。当时我就在Sun,对此亲眼目睹。真正Applets的是几年前在Java插件和Java Web Start中的一个严重的安全漏洞。那个漏洞造成了全世界的恐慌,于是人们都不再使用Java applets和Webstart应用了。

  其他Java桌面应用依然能够开发,而且NetBeans和Eclipse IDE之间的竞争现在依然火热。但这个领域的Java开发已经是一潭死水了,而且除了一些开发者工具之外,已经很少见到基于Java的应用程序了。

  JavaFX是十年前Sun为对抗iPhone而提出的方案。它计划支持在手机中的Java平台上开发富界面的应用程序,从而将Flash和iOS应用程序出市场。结果没有发生。JavaFX依然有人使用,但并不像它的那么火热。

  Java戒指,是早期的一次Java ONE会议的东西。这些戒指上包含芯片,内部完全是用Java实现的。在JavaONE上的主要用途是解锁大厅里的电脑。

  如今,开发服务器端代码有许多选择。我们不必再被局限在“P语言”(Perl,PHP,Python)和Java中,因为我们有Node.js、Ruby、Haskell、Go、Rust以及其他很多语言。现在的开发者能享受到许多快乐。

  至于为什么我这个Java死忠倒向了Node.js,显然是因为我喜欢使用Node.js编程时的感。Java已经成为负担,而使用Node.js却没有这种负担。如果有人雇我写Java当然我还会接受,因为我想赚钱。

  每个应用程序都有真实的需求。只因为喜欢Node.js就一直使用Node.js的态度显然不可取,选择一种语言或框架必然有技术上的原因。例如,我之前的一些工作涉及了XBRL文档。由于最好的XBRL库是用Python实现的,因此要完成项目,就必须学习Python。

  作者:David Herron,软件工程师,技术作价,喜欢Node.js和清洁能源技术。《Node.js Web 开发》一书的作者。

  文章由325棋牌提供发布

关键词:java对数据库
0
0
0
0
0
0
0
0
下一篇:没有资料

相关阅读

网友评论 ()条 查看

姓名: 验证码: 看不清楚,换一个

推荐文章更多

热门图文更多

最新文章更多

关于联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助

郑重声明:本站资源来源网络 如果侵犯了你的利益请联系站长删除

CopyRight 2010-2012 技术支持 FXT All Rights Reserved