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

易娃 等级 336 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年的时
Golang高并发抓取HTML图片
版权所有,转载请注明:http://www.lenggirl.com/language/gopicture.html(https://links.jianshu.com/go?tohttp%3A%2F%2Fwww.lenggirl.com%2Flanguage%2Fgopicture.html) 使用准备 1.安装Golang 2.
【Golang】Golang + jwt 实现简易用户认证
<p本文已同步发布到我的个人博客:<a href"https://links.jianshu.com/go?tohttps%3A%2F%2Fglorin.xyz%2F2019%2F11%2F23%2FGolangjwtsimpleauth%2F" target"_blank"https://glorin.xyz/2019/11/23/Golang
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】Go入门及进阶书籍推荐
Go入门教程全集 链接: https://pan.baidu.com/s/1mWD7DpRa56WXi7WmNaohOg(https://pan.baidu.com/s/1mWD7DpRa56WXi7WmNaohOg) 提取码: ki1e Cloud.Native.Go.pdf C和指针.pdf C面向对象多线程编程.pdf Design Patt
【Golang】GoWeb框架之Gin-简明教程
Gin 简介 Gin is a HTTP web framework written in Go (Golang). It features a
【程序人生】毕业入职后,C++转Go语言工作半年感受
我在大学期间就听说了Go并学习了一段时间,坦白的说,那时候对Go是比较无感的,因为并 没有看到Go特别亮眼的地方,可能和我使用C、C、Java有关,这三
Golang中常用的字符串操作
Golang中常用的字符串操作 一、标准库相关的Package go import( "strings" ) 二、常用字符串操作 1. 判断是否为空字符串 1.1 使用“”进行判断 思路:直接判断是否等于""空字符串,由于Golang中字符串不能为 nil,且为值类型,所以直接与空字符串比较即可。 举例: go
深入理解 Go Slice
(https://imghelloworld.osscnbeijing.aliyuncs.com/0ce8a8773a658d4b843e5796a0dbf001.png) image 原文地址:深入理解 Go Slice(https://github.com/EDDYCJY/blog/blob/master/golang/pkg/20
Go VS Java:一位资深程序员对两种语言的解读
导读:对于软件开发的编程语言,其实没有万能灵药。 本文作者详细介绍了他使用Java和Go这两种编程语言,一个是传统语言,一个是新兴语言的工作方式。 image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/0f0509de2420894d6c75e8678081e0cd.png)
我的golang基础
库查询 https://gowalker.org/你应该$HOME/.profile文件增加下面设置。 搭建go的环境 step1:去golang的官网下载go的安装包 windows:go1.9.2.....msi mac:go1.9.2......pkg 双击傻瓜式安装 linux:go1.9.2.linuxamd64.tar.gz 默认到下
go语言开发入门:GO 开发者对 GO 初学者的建议
以促进 India 的 go 编程作为 GopherConIndia 承诺的一部分。我们采访了 40 位 Gophers(一个 Gopher 代表一个 GO 项目或是任何地方的 GO 程序员),得到了他们关于 GO 的意见。如果你正好刚刚开始 go 编程,他们对于我们一些问题的答案可能会对你有非常有用。看看这些。应该做:通读 the Go standard
go mod环境搭建
前言go mod 是golang最新的模块依赖管理的工具,推荐使用。 go 1.11通过设置环境变量GO111MODULE来决定是否启用 go1.13已经默认支持,以下以1.13为例配置环境变量 export GOPROXY 或 export GOPROXY export GOPATH"/Users/XXX
GO开发[一]:golang语言初探
一.Golang的安装 1.https://dl.gocn.io/ (国内下载地址) (https://imghelloworld.osscnbeijing.aliyuncs.com/658c5d13c377