Go VS Java:一位资深程序员对两种语言的解读

易娃 等级 643 0 0

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

本文作者详细介绍了他使用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 VS Java:一位资深程序员对两种语言的解读

可以利用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 *singleton
var once sync.Once
func 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 main
importt "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的工具集则是相同的。

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

收藏
评论区

相关推荐

Golang高阶:Golang1.5到Golang1.12包管理
版权所有,转载请注明:http://www.lenggirl.com/go/gomod.html(https://links.jianshu.com/go?tohttp%3A%2F%2Fwww.lenggirl.com%2Fgo%2Fgomod.html) 1. 前言 Golang 是一门到如今有十年的静态高级语言了,2009年的时
godoc 命令和 golang 代码文档管理
介绍 godoc 是 golang 自带的文档查看器,更多的提供部署服务 go doc 和 godoc 在 golang 1.13 被移除了,可以自行安装 golang.org go1.13 godoc(https://links.jianshu.com/go?tohttps%3A%2F%2Fgolang.org%2Fdoc%2Fg
为什么GOPROXY对Golang开发如此重要
为什么GOPROXY对Golang开发如此重要 引言 从Go 1.13开始,Go Module作为Golang中的标准包管理器,在安装时自动启用,并附带一个默认的GOPROXY。 但是对于其他的GOPROXY选项,比如JFrog GoCenter,以及你自己的Go Module包,你需要在公众视野中保持安全,你应该选择什么样的配置? 你怎样才能
【Golang】GoWeb框架之Gin-简明教程
Gin 简介 Gin is a HTTP web framework written in Go (Golang). It features a
Go 语言编程 — go mod 依赖包管理
目录 == ### 文章目录 * 目录 * go mod 依赖包管理 * 使用 go mod go mod 依赖包管理 ============ go mod 是 Golang 1.11 版本引入的依赖包管理工具。其中,Golang 对 Modules 的定义:Modules 是相关 Go Packages 的集合,是源代码交换和版本控制
Go语言基础之并发
转载自[https://www.liwenzhou.com/posts/Go/14\_concurrence/](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.liwenzhou.com%2Fposts%2FGo%2F14_concurrence%2F) Go语言基础之并发 ==
Go语言规范(介绍)
Google发布Go语言已经有一段时间了,但是还少有在论坛看到关于Go的讨论,几个Go语言的BBS也冷冷清清的,国内程序员对 Go语言 似乎还持观望态度。在看了它的一点特性和一段时间的试用之后,我感受到了 Go语言 给我带来的乐趣,个人觉得 Go语言 以后将会有很好的发展前景,我已经是等不及了,我要好好学习一下这门语言,当然还有对里面的大牛们产生的崇拜!^\
go mod 无法自动下载依赖包的问题
go 11以后启用了go mod功能,用于管理依赖包。 当执行`go mod init`生成`go.mod`文件之后,golang在`运行`、`编译`项目的时候,都会检查依赖并下载依赖包。 在启动了`go mod`之后,通过`go mod`下载的依赖包,不在放在`GOPATH/src`中,而是放到`GOPATH/pkg/mod`中。 比如我当前的`GO
go系列(3)
这篇讲讲如何在beego框架使用redis。 golang中比较好用的第三方开源redisclient有: * go-redis * 源码地址:[https://github.com/go-redis/redis](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgith
go语言入门【基本类型和语法 一】
GOLANG ------ 入门,废话不说,要知道它是什么,自己百度谷歌就OK的啦 首先,安装环境 ------- 本人的测试环境是centos7 下载编译后版本加入到环境变量即可 GO有个坑的地方是对gopath的设置,类似于JAVA的CLASS\_PATH,但是针对每个项目得重新设置 编译工具 ---- 本人用的编译器是IDEA,对头,就是JA
vs code 下安装golang支持
1)安装gocode go get -u -v github.com/nsf/gocode 2)安装godef go get -u -v github.com/rogpeppe/godef 3)安装golint go get -u -v github.com/golang/lint/golint 4)安装go-find-references g
Archlinux下Visual Studio Code配置Golang开发环境
一、Golang的安装 ----------- GoLang安装并验证一下: [cox@localhost ~]$ sudo pacman -S go [cox@localhost ~]$ go version go version go1.8.3 linux/amd64s 要注意,Golang的安装要确保两个环境变量,一个是G
Golang 开发环境搭建
Golang 是 Google 发布的开发语言,Go 编译的程序速度可以媲美 C/C++。 安装 -- sudo apt-get install golang sudo apt-get install golang-go.tools 使用 -- * 编译运行程序 go run main.go * 查看命令文
Golang1.7 Goroutine源码分析(转)
一、       Golang简介 **1.1概述** Golang语言是Google公司开发的新一代编程语言,简称Go语言,Go 是有表达力、简洁、清晰和有效率的。它的并行机制使其很容易编写多核和网络应用,而新奇的类型系统允许构建有弹性的模块化程序。 Go 编译到机器码非常快速,同时具有便利的垃圾回收和强大的运行时反射。而他最广为人知的特性便是语言层面
Go编程语言前景怎么样?参加培训好就业吗
Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。不仅可以开发web,可以开发底层,目前知乎就是用golang开发。区块链首选语言就是go,以-太坊,超级账本都是基于go语言,还有go语言版本的btcd. ![](https://oscimg.oschina.net/oscne