写点什么

阿里架构师总结 Go 语言和 java 语言之间的对比联系

用户头像
hanaper
关注
发布于: 刚刚
阿里架构师总结Go语言和java语言之间的对比联系

go 语言和 java

一句话概括 go 语言和 java 的特点

  • Java「就业最好」:岗位多,工资高。这个趋势也许会持续 5-10 年。

  • Go 语言「最有前途」,语言优秀,家底丰厚,但暂时就业岗位还不是很多。

语言特性

  • Go 是一种命令式语言,

  • Java 则是一种声明式语言。

Go 语言

GO 作为编程界的小鲜肉。进年来 Go 社区非常的活跃,高并发能力无人能及。即具有像 Python 一样的简洁代码、开发速度,又具有 C 语言一样的执行效率,优势突出。

Go 语言学习路线图: https://github.com/0voice/Introduction-to-Golang

还是从根本去了解 Go 语言吧

1. 设计 Go 语言是为了解决当时 Google 开发遇到的问题

  • 大量的 C++代码,同时又引入了 Java 和 Python

  • 成千上万的工程师

  • 数以万计行的代码

  • 分布式的编译系统

  • 数百万的服务器

2. Google 开发中的痛点

  • 编译慢

  • 失控的依赖

  • 每个工程师只是用了一个语言里面的一部分

  • 程序难以维护(可读性差、文档不清晰等)

  • 更新的花费越来越长

  • 交叉编译困难

3. 如何解决当前的问题和痛点?

  • Go 希望成为互联网时代的 C 语言。多数系统级语言(包括 Java 和 C#)的根本编程哲学来源于 C++,将 C++的面向对象进一步发扬光大。但是 Go 语言的设计者却有不同的看法,他们认为值得学习的是 C 语言。C 语言经久不衰的根源是它足够简单。因此,Go 语言也是足够简单。

  • 所以,他们当时设计 Go 的目标是为了消除各种缓慢和笨重、改进各种低效和扩展性。Go 是由那些开发大型系统的人设计的,同时也是为了这些人服务的;它是为了解决工程上的问题,不是为了研究语言设计;它还是为了让我们的编程变得更舒适和方便。

  • 但是结合 Google 当时内部的一些现实情况,如很多工程师都是 C 系的,所以新设计的语言一定要易学习,最好是类似 C 的语言;20 年没有出新的语言了,所以新设计的语言必须是现代化的(例如内置 GC)等情况。最后根据实战经验,他们向着目标设计了 Go 这个语言。

4. Go 语言的特色

  • 没有继承多态的面向对象

  • 强一致类型

  • interface 不需要显式声明(Duck Typing)

  • 没有异常处理(Error is value)

  • 基于首字母的可访问特性

  • 不用的 import 或者变量引起编译错误

  • 完整而卓越的标准库包

  • Go 内置 runtime(作用是性能监控、垃圾回收等)

5. Go 语言的优势

5.1 学习曲线容易

Go 语言语法简单,包含了类 C 语法。因为 Go 语言容易学习,所以一个普通的大学生花几个星期就能写出来可以上手的、高性能的应用。在国内大家都追求快,这也是为什么国内 Go 流行的原因之一。

5.2 效率:快速的编译时间,开发效率和运行效率高

开发过程中相较于 Java 和 C++呆滞的编译速度,Go 的快速编译时间是一个主要的效率优势。Go 拥有接近 C 的运行效率和接近 PHP 的开发效率。

5.3 出身名门、血统纯正

之所以说 Go 出身名门,从 Go 语言的创造者就可见端倪,Go 语言绝对血统纯正。其次 Go 语言出自 Google 公司,Google 在业界的知名度和实力自然不用多说。Google 公司聚集了一批牛人,在各种编程语言称雄争霸的局面下推出新的编程语言,自然有它的战略考虑。而且从 Go 语言的发展态势来看,Google 对它这个新的宠儿还是很看重的,Go 自然有一个良好的发展前途。

5.4 自由高效:组合的思想、无侵入式的接口

Go 语言可以说是开发效率和运行效率二者的完美融合,天生的并发编程支持。Go 语言支持当前所有的编程范式,包括过程式编程、面向对象编程、面向接口编程、函数式编程。程序员们可以各取所需、自由组合、想怎么玩就怎么玩。

5.5 强大的标准库

这包括互联网应用、系统编程和网络编程。Go 里面的标准库基本上已经是非常稳定了,特别是我这里提到的三个,网络层、系统层的库非常实用。**Go 语言的 lib 库麻雀虽小五脏俱全。**Go 语言的 lib 库中基本上有绝大多数常用的库,虽然有些库还不是很好,但我觉得不是问题,因为我相信在未来的发展中会把这些问题解决掉。

5.6 部署方便:二进制文件,Copy 部署

这一点是很多人选择 Go 的最大理由,因为部署太方便了,所以现在也有很多人用 Go 开发运维程序。

6. Go 开源项目

docker

: 无人不知的虚拟化平台,开源的应用容器引擎,借助该引擎,开发者可以打包他们的应用,移植到任何平台上。

golang

: go 本身,也是用 go 语言实现的,包括他的编译器,要研究 go 源代码的可以看此项目录

kubernetes

: Google 出品,用于调度和管理 docker 的开源容器管理系统,利用他,可以方便的管理你的 docker 实例,哪怕非常多,也是目前最流行的 docker 管理系统。

gogs

: 一款基于 git 的代码托管系统,类似于 github 和 gitlab,不过其小巧易用,功能强大,部署方便,也有不少用户在使用。

syncthing

: 开源的文件同步系统,它使用了其独有的对等自由块交换协议,速度很快,据说可以替换 BitTorrent Sync。

grafana

: 一款开源监控度量的看板系统,可以接 Graphite、Elasticsearch、InfluxDB 等数据源,定制化很高。

etcd

: 一款分布式的,可靠的 K-V 存储系统,使用简单,速度快,又安全。

hub

: 一款更便捷使用 github 的工具,包装并且扩展了 git,提供了很多特性和功能,使用和 git 差不多。

influxdb

: 可伸缩的数据库,使用场景主要用来存储测量数据,事件点击以及其他等实时分析数据,用来做监控性能很不错。

caddy

: 快速的,跨平台的 HTTP/2 Web 服务器。

beego

: 国产开源的高性能 Web 框架,让你快速的开发 Go Web 应用服务,谢大主笔。

7. Golang 资料补给包

包含文章,书籍,作者论文,理论分析,开源框架,云原生,大佬视频,大厂实战分享 ppt​github.com

Go 与 java 对比

语法对比

异常错误处理对比

错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是 。

错误和异常需要分类和管理,不能一概而论。错误和异常的分类可以以是否终止业务过程作为标准错误是业务过程的一部分,异常不是不要随便捕获异常,更不要随便捕获再重新抛出异常。

Java 中,Throwable 是所有错误(Error)和异常 (Exception) 的基类,整的来说,它们都是程序运行过程中可能出现的问题,区别在哪里呢? Exception 是可预料的,而 Error 是不可预料的。Exception 我理解为在程序运行中正常情况下意料之中发生的事,是可以被程序员处理,补救,有机会回到正常处理流程的,而 Error 在程序运行中非正常情况下发生后是无法被处理,恢复,比如内存溢出,栈溢出等。

Go 中引入 error 接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含 error。error 处理过程类似于 C 语言中的错误码,可逐层返回,直到被处理。

Golang 中引入两个内置函数 panic 和 recover 来触发和终止异常处理流程,同时引入关键字 defer 来延迟执行 defer 后面的函数。

Recover 之后还是被返回 Error 所处理,什么含义呢,就是在意料之外的 panic 发生时,在 defer 中通过 recover 捕获这个恐慌,转化为错误通过方法返回值告诉方法调用者。

其实从字面意思,Go 中弱化了异常,一切皆错误,都被包装成类似 Code,Message 的形式返回方法调用者,直到有调用者去处理, 这也是 Go 的设计精髓,简化没必要存在的。

错误处理

在 Go 代码中 if err!=ni 随处可见,尤其是业务代码开发中。Go 没有类似 try catch 这样的语句,Go 对错误的价值观是可编程,我们看下面的代码:

go 错误处理result1,err := func1()if err != nil{    return nil,,err}result2,err := func2()if err != nil{    return nil,err}result3,err := func3()if err != nil{    return nil,err}return result1+result2+result3,nil
java错误处理try{ result1 = method1(); reuslt2 = method2(); result3 = method3(); return result1+result2+result3;}cache(Exception e){ log.error(e); return null;}
复制代码

从顺序角度来看,Go 更能被理解,每一个方法都有一个结果值和一个可能发生的错误值,Go 中程序员对 err 有更多的操作空间,有更多的可编程性。Java 需要更多的语法和更多理解,Java 中相对可编程性弱化了许多。

异常处理

Java 中通过 throw new 抛出一个异常,通过 tryca che 捕获,而 Go 中通过 panic 抛出一个恐慌,通过 defer 和 recover 来处理,实现类似的功能。代码如下:

func test() (err error){    defer func(){        if e:=recover(); e != nil{             err = e.(error)        }    }()        panic("发生恐慌了")    return err}
public Result test(){ try{ throw new RuntimeException("发生异常了") }cache(Exception e){ //处理错误 1 打印错误,忽略 2 throw e,继续抛出 //转化为code,message 3 new Result(code,e.message) }}
复制代码

对 Java 异常包装后是否和 Go 的错误设计有异曲同工之妙,同样的 Go 通过 panic 和 defer,recover 也可以为 try,cache, throw 这样的处理,但语言层面的设计的本质是不一样的。

Java 中对异常和错误有一个比较清晰的边界,通过类继承体系进行隔离,错误并不在程序员的考虑范围之内,通过异常体系和控制流程来实现业务逻辑,往往也容易被滥用;而 Go 中并没有,且弱化了异常的概念,并提供了将异常转化为错误的方法。一切皆错误,拥有更好的可编程性,但同时也带来诸如 iferr!=nil 的这样的代码到处都是,不同的编程语言对异常错误体系设计不一样,也代表不同开发者的思想,没有对与错。

并发包对比

内存管理对比

堆内存管理

Go 语言的内存分配器就借鉴了 TCMalloc(Thread-Caching Malloc) 的设计实现高速的内存分配,它的核心理念是使用多级缓存根据将对象根据大小分类,并按照类别实施不同的分配策略。Go 语言的内存分配器会根据申请分配的内存大小选择不同的处理逻辑,运行时根据对象的大小将对象分成微对象((0, 16B))、小对象([16B, 32KB])和大对象((32KB, +∞))三种。内存分配器不仅会区别对待大小不同的对象,还会将内存分成不同的级别分别管理,TCMalloc 和 Go 运行时分配器都会引入线程缓存(Thread Cache)、中心缓存(Central Cache)和页堆(Page Heap)三个组件分级管理内存。这些都可以理解为基于基础字段类型、工程中对象大小现状等做的一些约定优化。

为了让内存回收更加高效,从 Sun JDK 1.2 开始对堆采用了分代管理方式。可以氛围年轻代和老年代。当年轻代需要回收时会触发 Minor GC(也称作 Young GC)。对象在被创建时,内存首先是在年轻代进行分配(注意,大对象可以直接在老年代分配)。年轻代由 Eden Space 和两块相同大小的 Survivor Space(又称 S0 和 S1)构成。老年代用于存放在年轻代中经多次垃圾回收仍然存活的对象,可以理解为比较老一点的对象。

垃圾回收

Java 和 Go 语言都有垃圾回收器,会自动清理堆内存中已经不再使用的 dead 内存对象。两者垃圾回收的算法基础都是标记清除(Mark-Sweep)算法。标记清除收集器是跟踪式垃圾收集器,其执行过程可以分成标记(Mark)和清除(Sweep)两个阶段:标记阶段 — 从根对象出发查找并标记堆中所有存活的对象;清除阶段 — 遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表。

go 为了解决原始标记清除算法带来的长时间 STW(Stop The World),多数现代的追踪式垃圾收集器都会实现三色标记算法的变种以缩短 STW 的时间。详细参考 go 内存总结。该算法目前在 java 的 GC 中还未采用。

Java 中针对不同代的内存特定,提出了包含线性、并发、并行标记清除和 G1 四个垃圾收集器。可以在命令行中选择。因为他需要平衡其他各种因素,因此没有一个 GC 算法的目标能将暂停时间降低到 Go 水平。可以通过重新启动程序在 GC 之间切换,因为编译是在程序运行时完成(这里指 JIT 编译器),所以不同算法所需的不同内存屏障可以根据需要编译和优化到代码中。Go 的垃圾收集器是一个并发的,三色,标记扫描收集器,这是一个由 Dijkstra 在 1978 年首次提出的想法。

Java GC 默认算法是吞吐量收集器(throughput collector),默认情况下没有任何暂停时间目标。这种默认选择也是人们认为 Java GC 有点吸引力的一个原因:开箱即用,它试图使您的应用程序尽可能快地运行,并尽可能少的内存开销,而暂停时间不是该算法首要考虑的。Go 将暂停时间优化作为首要目标,以至于它似乎愿意将程序减慢至任何数量级,以获得较短暂停。

发布于: 刚刚阅读数: 2
用户头像

hanaper

关注

还未添加个人签名 2018.05.07 加入

还未添加个人简介

评论

发布
暂无评论
阿里架构师总结Go语言和java语言之间的对比联系