导读:对于软件开发的编程语言,其实没有万能灵药。

本文作者详细介绍了他使用Java和Go这两种编程语言,一个是传统语言,一个是新兴语言的工作方式。

Go VS Java

实话说,我很喜欢Java这门语言。近几年来,我在公司里积累了大量关于EJB2,DB2与Oracle等后端开发的专业知识。

现在我转向到基于自然语言处理的开发方向,如Spring Boot、Redis、RabbitMQ、Open NLP还有UIMA等技术。

因此说来,我选择的语言是Java,这个语言一直有旺盛的生命力,写起程序来也很有意思。

开始测试Go!

在2017年初,我接到了一个很有意思的项目。这个项目围绕着自动化系统来监控农作物和花花草草等植物们。

原先的开发团队源代码部署在三个不一样的系统中:Windows,MacOS和ARM,他们使用Go语言做为网关。

坦白讲,当时我对Go语言一点儿也不熟悉,随着项目的发展,我的工作内容包括对新技术的学习以及产品实施。项目现有代码库结构复杂,我面临的挑战比较大。使用Go写的针对于物联网平台支持程序,要在三个异构系统上部署、测试与运维。这个系统使用单例模式编写,系统耦合严重,模块之间相互杂揉,可以说是一处错误,多处崩溃。我用Java的开发风格重新构建了网关的新版本,但后让我弄的比原来更丑陋、混乱了。

我后来升职为一家公司任技术总监。

我试着不再使用Java的方式来开发Go了,尽量更多的使用Go的语言特性来做,全面拥抱该语言。

如此下来我才真正发现,Go的确是一个创新且全面的编程语言,我的团队开始将它用它来开发各种各样的项目。

不容置疑的是,与任何编程语言一样,Go有优点也有不少缺点,我这个人不太会撒谎,有时候我还蛮想念Java的。

依照以往我的编程经验,软件开发方面没有灵丹妙药,编程语言就是典型。以下我来讲一下心得体会,使用一门传统语言和创新语言新手是怎么干活的。

Go与Java的相似之处

Go语言与Java都从属于C语言家族,既然是远房亲戚,它们有着相似的语法。所以,Java开发者阅读Go写的代码并不会太难,反之亦然。

Go语句在语句末尾并不使用;分号结束。不用担心,除了极少数情况,我阅读Go写的代码反倒感觉更清晰易读。

Go与Java都使用我最喜欢的功能之一,那就是垃圾收集器(GC),以防止内存泄漏。

C/C++程序员都知道,自己在写程序时要注意避免内存泄漏,垃圾回收机制让内存管理自动化,从而简化程序员在代码中处理类似的功能。

Go语言的GC工具的表现很优秀,它的处理时间在1.8版本中不到1ms即可完成。

在Go语言中,其GC的设置参数并不多,有一个唯一的GC变量用来设置初始时垃圾回收目标的百分比。在Java中,有4个不同的垃圾收集器,每一个都要有大量的设置工作。

Java和Go都被视为跨平台语言,但是Java需要Java虚拟机(JVM)来解析编译后的代码,而Go可以简单地将代码编译为给定平台的二进制文件。

Java与Go相比,Go比Java依赖度更低,因为Go每次为一个平台编译代码时都要创建一个二进制文件。从测试和DevOps角度来看,分别编译不同平台的二进制文件非常的费时,有时候使用GO组件时,跨平台的Go编译在一些情况下并不起作用。但是,使用Java则可以在有JVM的任何地言使用一个Jar。

Go占用内存很小,而且并不需要安装和管理虚拟机的关联依赖,以及复杂的注意事项。

接下来谈一下反射功能。Java的反射功能很强大,而Go的反射更偏复杂。Java是一种纯面向对象语言,所有内容都被视为对象。

在Java使用反射,需要为对象创建一个类,并从此类中获取所需要的信息。如下代码所示:

Class cls = obj.getClass();Constructor constructor = cls.getConstructor();Method[] methods = cls.getDeclaredFields();

接下来就可以访问构造函数、方法和属性,然后去调用和设置它们。

在Go语言中,则没有类的概念,它的结构中仅包含已声明的字段。因此,我们需要它的“反射”包来提供相关信息。

type Foo struct {  A int `tag1:"First Tag"  tag2:"Second Tag"`  B string}f := Foo{A: 10, B: "Salutations"}fType := reflect.TypeOf(f)switch t.Kind(fType)    case reflect.Struct:        for i := 0; i < t.NumField(); i++ {          f := t.Field(i)          // ...        }}

我认为,这并不是一个大问题,因为Go中没有用于结构的构造函数,因此结果是许多原始类型,且必须分别处理,还需要认真考虑指针。

在Go中,我们还可以通过指针或值传递某些东西。Go语言结构具有字段的功能,而不是方法。

再来说一下两个语言的辅助功能。

Java具有Private、Protected以及Public这3个修饰符,用来对数据、方法和对象提供不同范围的存取。

Go语言中,有着和Java类似的语句,它们是exported/unexported修饰符。在Go中如果没有修饰符,以大写字母开头的所有内容都能够导出,并且在其它软件包中可见。unexported,以小写变量或函数仅在当前包中可见。

Go与Java大方面的差异

Go语言并不是一种面向对象(OOP)语言。它没有像Java中的继承,没有通过继承这一特性实现系统的多态性。

Go语言没有对象,只有结构体。但它可以通过提供接口实现一些面向对象的模式。同样道理,可以将将结构彼此嵌入,但是嵌入式结构无法访问宿主结构的数据和方法。

Go中使用组合,而不使用继承来组合一些所需的行为和数据。

Go是一种命令式语言,而Java则是一种声明式语言。

Go中没有像依赖式注入那样的东西,在Go中必须将所有内容明确的包装在一块。因此,在Go中编程末尽量少用一些魔术方法,一切都是可见的。

Go程序员要了解Go代码如何使用内存、文件系统以及其它关联资源的全部机制。

从另一方面,Java需要开发人员更多的关注自定义开发的业务逻辑部分,诸如如何创建、过滤、更改和存储数据。

从系统基础架构和数据库管理系统而言,所有这些都是通过配置和通过Spring Boot等通用框架来完成。

人们总是矛盾心理,我们对基础架构的部分感到枯燥乏味,因此这部分功能交给框架。这会给我们带来方便,但对于程序员来说,控制权在框架,也限制人们对整个流程优化的能力。

再讨论一下两个语言的变量定义,以及顺序。在Java中定义变量类似于以下:

String name;

在Go语言,这样来写:

name string

我在第一次使用Go时,没有;号的代码让我有些困惑,就像写文章没写句号一样,感觉没有写完。

使用Go语言的优势

Go有着简单且且优雅的并发能力。Go语言有着强大的并发模型,称之为“通信顺序过程”或CSP。在Go中使用n-to-m嗅探器,该嗅探器允许n个系统线程中发生m个并发执行。

可以利用Go语言的关键字:go(与该语言的名字相同)启动例程。比如下面的字符串:

go doMyWork()

此时函数doMyWork()将同时开始执行。

在Go中进程之间的通信可以通过共享内存(并不推荐)和共享通道来完成。它允许开发者使用GoMaxProcs环境变量定义一个多内核的健壮且流畅有并发系统。

Go提供了一种特殊模式-race(竞赛)来运行二进制文件,并同时检查运行情况。通过此机制来证明软件是并发安全的。

run-race myapp.go

以上命令格式,即以竞赛检测模式运行一个应用程序。

我个人很喜欢Go提供的即开即用功能(golang.org/dl),它提供一个很好的例子,如sync同步功能(golang.org/pkg/sync)包。

比如针对实现Singleton模式实现,可以如下代码表示:

package singleton import ("sync")type singleton struct { }var instance *singletonvar once sync.Oncefunc GetInstance() *singleton {  once.Do(func() {    instance = &singleton{}  })  return instance}

同步包还为并发实现映射、互斥锁、条件变量和等待提供了一种结构。它的另一个包atomic(golang.org/pkg/sync/atomic)提供了并发安全转换与数学运算。

实际上,Go提供了并发代码所需要的全部。

Duck typing

“如果你说话像鸭子,走路像鸭子,那它一定就是只鸭子”。这句话在Go的世界里是正确的。

在Go语言中,不需要定义某种接口,直接实现结构即可。在Java中,对象比较显式声明并实现了接口。

探查器。Go提供的性能分析工具,使用非常方便、快捷和容易。Go中的事件探查器有助于显示程序中的内存分配和CPU使用情况,还可以在可视化图形中加以说明,这对优化应用程序特别有用。

Java可以从Java VisualVM这些探查器中来开始,但它们不像Go的探查器那样简单,而且这些工具的功效取决于JVM,其获取的统计信息和垃圾收集与JVM的具体工作有关。

C 与Go

Go中允许对C语言进行简单且强大的集成。开发者可以在Go项目中编写有C语言代码的平台相关应用。从底层来讲,CGo可以让开发人员创始调用C代码的Go程序包。为了排除/包含指定平台的C代码段,如各种构建器选项,这些代码段可以实现应用程序的多平台化。

函数作为参数使用

Go函数可以用作变量,传递给另一个函数或用作结构的字段。Go语言的这种多功能性的确让人耳目一新。

而Java是从1.8开始结合了lambda的使用,但它并不是真正的函数,而是一个单功能的对象。它的这一特性就是想实现Go中使用函数的行为,而Go在一开始就

存在于语言特性中。

明晰的代码风格。在Go语言社区中,有着众多热情和能力强大的支持者,社区中有大量对Go的使用示例和操作的最佳实践和方法。

其地址为https://golang.org/doc/effective_go.html

在Go的函数中可以返回多个参数,这是一个非常有用和优秀的方式。如下代码:

package mainimportt "fmt"func returnMany() (int,string,error){return 1,"example",nil}func main(){i,s,err :=returnMany()fmt.Printf("Returned %s %s %v",i,s,err)}

Go语言的缺点

Go除了接口外,没有多态特性。Go中没有多态性,这表示如果在同一个程序包中,有两个函数具有不同的参数,但是含义相同,这时必须给它们修改为不同的名称。请看如下之代码:

func makeWorkIntt(number int){  fmt.Printf("Work done number %d",number)}func makeWorkStr(title string){  fmt.Printf("Work done title %s",title)}

其实这些方法都做同样的事情,只是方法名称不同,而且代码比较丑陋。

此外,在Go中没有继承的多态性。如果嵌入到结构,则嵌入式结构只知道自己的方法,而不会知道宿主结构的方法。这对于一些有其它语言经验开发者有一定的挑战性,他们在之前使用的OOP语言主要使用继承处理,当使用GO语言时会有一些困惑。

随着时间的流转,我自己开始意识到继承式的多态化处理只是一种思维方式,结构式处理很可靠,也更明显,且运行时间可变。

关于错误处理,处理什么返回错误信息,以及如何返回错误。作为开发者,我们需要每次都返回错误并传递相关错误。有时候错误会隐藏,这确实是麻烦的所在,要记住检查错误并把它们传递出去。

在Java中异常处理要方便得多,比如RuntimeException,都可以不加入函数的签名中即可使用。

public void causeNullPointerException() {  throw new NullPointerException("demo");}/*...*/try {  causeNullPointerException();} catch(NullPointerException e) {  System.out.println("Caught inside fun().");  throw e; // rethrowing the exception}

此外,Go语言没有泛型支持,虽然这一特性会增加复杂性,Go的创建者也认为代价高昂。在Go语言创建时,必须针对不同的类型重复使用自己的或生成代码。

没有注释

在Go中可以用代码生成部分注释,但是不幸的是,在运行时注释根本不能替换,这是有原因的——Go不是声明性的,且代码中不应包含任何魔术。

我喜欢在Java中使用注释,它们让代码更加优雅、简单、简约。这些注释可以生成大量文件,在做HTTP结点提供某些元数据时非常有用。在GO语言中,必须手动或直接制作Swagger文件,或作为结点功能提供特殊注释。Java中的注释是一种魔术方式,人们通常不必关心它们的工作方式。

再来谈一下Go中的依赖管理。在本文之前我曾写过一篇GO依赖项管理的文章。Go依赖管理环境在相当长一段时间中存在一些麻烦。最初,除了“Gopgk”外没有任何依赖项管理,后来又发布“Vendor”后被“vgo”取代。现在可以手动和使用go命令(如go get命令)来修改go.mod文件描述符来处理。

不幸的是,这种修改会引发依赖关系不稳定。

Go没有开箱即用的源镜像依赖关系管理机制,而Java有着诸如Maven和Gradle之类著名的依赖管理声明类工具。它们也可能用来构建,部署和处理其它集成用途。

我们在实际开发中使用Makefile、Docker-composer和bash脚本自定义构建所需的依赖关系管理,会让持续交付和集成的过程、稳定性变复杂。

在Go中,引用软件包的名称要包含托管的GitHub域名。例如:

import “github.com/pkg/errors”

这块实在有点奇怪,而且非常不方便,如果不更改整个项目代码库的导入,就无法用自己的实现替换其它人的代码。

而在Java中,导入通常以公司名字开头。像这样:

import by.spirascout.public.examples.simple.Helper;

当然,我想在GO中的这些依赖管理问题都只是暂时的,将来会以最有效的方式解决。

总结

Go语言中最有趣的一块是,它遵循一种代码命名准则,称为代码可读性方法的心理学。使用Go语言,可以使用单独的方法来构建清晰且可维护的代码,虽然看起来像是一些单词组合在一起,但实际上清晰可读。

一些使用Go进行Web开发的公司,它们将项目展示给我,代码体现了Go的快速,强大且易于理解,非常适合小型服务和并发处理。而对大型,复杂的系统,比如功能更复杂的服务以及微服务系统,Java暂时保持在世界顶级编程语言的地位。

尽管Java SE已经成为Oracle的收费商品,而Go则是技术社区的孩子。已经产生多个品牌的JVM,而Go的工具集则是相同的。

可以确定的是,不同的任务需要不同的工具,语言也是如此。

作者:万能的大雄

关联阅读:

可能是世界上最简单的用 Go 来写 WebAssembly 的教程

Golang与Python,哪种编程语言更适合您?

PHP VS Golang,是一个艰难选择吗?

go语言和java比_Go VS Java:一位资深程序员对两种语言的解读相关推荐

  1. 转载:一位资深程序员大牛给予Java初学者的学习路线建议

    一位资深程序员大牛给予Java初学者的学习路线建议 java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来 ...

  2. (转)一位资深程序员大牛给予Java初学者的学习路线建议

    一位资深程序员大牛给予Java初学者的学习路线建议 java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来 ...

  3. 一位资深程序员大牛(4-5年经验)给予Java初学者的学习建议

    这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议? 今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每个阶段要 ...

  4. 心得丨一位资深程序员大牛给予Java初学者的学习路线建议

    Java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每 ...

  5. 一位资深程序员大牛给Java初学者的学习建议

    糖糖发现最近很多想要学习Java的小白或者转行到Java这个行业的伙伴们不知道怎样学习才能学得更多专业,今天糖糖给大家整理了一些前辈们的学习方法和技巧,以及常看的一些书籍.这些也非常适合初入Java行 ...

  6. 一位资深程序员大牛给予Java初学者的学习建议

    这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议? 今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每个阶段要 ...

  7. 一位资深程序员大牛给予Java初学者的学习路线建议,这样学年薪30万不是梦

    Java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每 ...

  8. 转载Java园长《一位资深程序员大牛给予Java初学者的学习路线建议》

    Java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每 ...

  9. 一位资深程序员大牛给予Java学习者的学习路线建议

    java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈 ...

最新文章

  1. ibatis主键自动生成
  2. python游戏创新大赛
  3. 嵌入式Linux裸机开发(六)——S5PV210时钟系统
  4. 5-4日 socket套接字
  5. 逆波兰计算器android源码简书,计算器的核心算法-JavaScript实现(逆波兰表达式)...
  6. 邮箱与手机做账号的验证实例
  7. python入门指南小说-Python 入门指南
  8. 微信昵称包含emoji表情,保存异常
  9. jquery捕捉ctrl+enter(回车)事件
  10. java、python什么意思_java和python是什么
  11. 如何将png图像转换成jpg格式呢?
  12. react加水印_【REACT】 水印生成方案
  13. 9.23 深度学习微调
  14. 前端程序员初步认识 docker
  15. GPON ITU-T G.xxx 标准协议下载
  16. 网络编辑要学些什么技能?
  17. VMware vSphere Replication 8.5部署及使用教程
  18. 贪心算法-杭电oj2037
  19. WinRAR 无法关联zip、rar等文件
  20. 对网络安全态势感知的理解和认识

热门文章

  1. Java之美[从菜鸟到高手演变]之Spring源码学习 - 环境搭建
  2. Atitit.vod 视频播放系统 影吧系统的架构图 架构体系 解决方案
  3. 通过NavMeshObstacle解决NavMesh防卡
  4. 通过修改Delphi 的 RTL,加快Delphi开发的应用程序速度和稳定性
  5. [Vue.js]实战 -- 电商项目(五)
  6. Python_装饰器Decorator
  7. LeetCode--34.在排序数组中查找元素第一个和最后一个位置(二分法)
  8. 深入理解深度学习中的激活函数
  9. 7-17 航空公司VIP客户查询 (10 分)
  10. c语言实现路由功能,前端路由的两种实现方式,内附详细代码