Golang与其他语言不同的九个特性

目录
  • 1. Go 总是在构建中包含二进制文件
  • 2. Go 没有针对程序依赖的集中托管服务
  • 3. Go 是按值调用的
  • 4. ‘defer' 关键字
  • 6. Go 有隐式接口
  • 7.错误处理
  • 8.并发
  • 9. Go 标准库

随着编程语言的发展,Go 还很年轻。它于 2009 年 11 月 10 日首次发布。其创建者Robert Griesemer
Rob Pike 和 Ken Thompson在 Google 工作,在那里大规模扩展的挑战激励他们将 Go 设计为一种快速有效的编程解决方案,用于具有大型代码库、管理由多个开发人员,具有严格的性能要求,并跨越多个网络和处理核心。
Go 的创始人在创建他们的新语言时也借此机会学习了其他编程语言的优点、缺点和漏洞。结果是一种干净、清晰和实用的语言,具有相对较少的命令和功能集。

在本文中,今天这篇文章将给大家介绍一下 Go 与其他语言不同的 9 个特性。

1. Go 总是在构建中包含二进制文件

Go 运行时提供内存分配、垃圾收集、并发支持和网络等服务。它被编译到每个 Go 二进制文件中。这与许多其他语言不同,其中许多语言使用需要与程序一起安装才能正常工作的虚拟机。

将运行时直接包含在二进制文件中使得分发和运行 Go 程序变得非常容易,并避免了运行时和程序之间的不兼容问题。Python、Ruby 和 JavaScript 等语言的虚拟机也没有针对垃圾收集和内存分配进行优化,这解释了 Go 相对于其他类似语言的优越速度。例如,Go 将尽可能多的存储在堆栈中,其中数据按顺序排列以便比堆更快地访问。稍后会详细介绍。

关于 Go 的静态二进制文件的最后一件事是,因为不需要运行外部依赖项,所以它们启动得非常快。如果您使用Google App Engine 之类的服务,这是一种在 Google Cloud 上运行的平台即服务,它可以将您的应用程序缩减到零实例以节省云成本,这将非常有用。当收到新请求时,App Engine 可以在眨眼间启动 Go 程序的一个实例。在 Python 或 Node 中的相同体验通常会导致 3-5 秒(或更长时间)的等待,因为所需的虚拟环境也会与新实例一起启动。

2. Go 没有针对程序依赖的集中托管服务

为了访问已发布的 Go 程序,开发人员不依赖集中托管的服务,例如Java 的Maven Central或JavaScript的NPM注册表。相反,项目通过其源代码存储库(最常见的是 Github)共享。在go install命令行允许以这种方式下载库。
为什么我喜欢这个功能?我一直认为像 Maven Central、PIP 和 NPM 这样的集中托管的依赖服务有点令人生畏的黑盒子,也许可以抽象出下载和安装依赖项的麻烦,但不可避免地会在依赖项错误时引发可怕的心跳停止发生。

此外,将的的模块提供给其他人就像将其放入版本控制系统一样简单,这是分发程序的一种非常简单的方式。

3. Go 是按值调用的

在 Go 中,当你提供一个原始值(数字、布尔值或字符串)或一个结构体(类对象的粗略等价物)作为函数的参数时,Go 总是会复制变量的值。

在 Java、Python 和 JavaScript 等许多其他语言中,原语是按值传递的,但对象(类实例)是按引用传递的,这意味着接收函数实际上接收的是指向原始对象的指针,而不是其副本。在接收函数中对对象所做的任何更改都会反映在原始对象中。

在 Go 中,结构体和原语默认按值传递,可以选择传递指针,通过使用星号运算符:

// 按值传递
func MakeNewFoo(f Foo ) (Foo, error) {
   f.Field1 = "New val"
   f.Field2 = f.Field2 + 1
   return f, nil
}

上述函数接收 Foo 的副本并返回一个新的 Foo 对象。

// 通过引用传递
func MutateFoo(f *Foo ) error {
   f.Field1 = "New val"
   f.Field2 = 2
   return nil
}

上面的函数接收一个指向 Foo 的指针并改变原始对象。

按值调用与按引用调用的这种明显区别使您的意图显而易见,并降低了调用函数无意中改变传入对象的可能性(当它不应该发生时(许多初学者开发人员很难做到这一点)握紧)。

正如麻省理工总结的:“可变性使得理解你的程序在做什么变得更加困难,并且更难以执行契约”

此外,按值调用显着减少了垃圾收集器的工作,这意味着应用程序更快、内存效率更高。这篇文章得出的结论是,指针追踪(从堆中检索指针值)比从连续堆栈中检索值慢 10 到 20 倍。要记住的一个很好的经验法则是:从内存中读取的最快方法是顺序读取,这意味着将随机存储在 RAM 中的指针数量减少到最少。

4. ‘defer' 关键字

在NodeJS 中,在我开始使用knex.js之前,我会通过创建一个数据库池来手动管理我的代码中的数据库连接,然后在每个函数中从池中打开一个新连接,一旦所需的数据库 CRUD 功能已完成。

这有点像维护噩梦,因为如果我没有在每个函数结束时释放连接,未释放的数据库连接的数量会慢慢增长,直到池中没有更多可用连接,然后中断应用程序。

现实情况是,程序经常需要释放、清理和拆除资源、文件、连接等,因此 Go 引入了defer关键字作为管理这些的有效方式。

任何以defer开头的语句都会延迟对它的调用,直到周围的函数退出。这意味着您可以将清理/拆卸代码放在函数的顶部(很明显),知道一旦函数完成它就会如此。

func main() {
    if len(os.Args) < 2 {
        log.Fatal("no file specified")
    }
    f, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    data := make([]byte, 2048)
    for {
        count, err := f.Read(data)
        os.Stdout.Write(data[:count])
        if err != nil {
            if err != io.EOF {
                log.Fatal(err)
            }
            break
        }
    }
}

在上面的例子中,文件关闭方法被推迟了。我喜欢这种在函数顶部声明你的内务处理意图的模式,然后忘记它,知道一旦函数退出它就会完成它的工作。

5. Go 采用了函数式编程的最佳特性

函数式编程是一种高效且富有创造性的范式,幸运的是 Go 采用了函数式编程的最佳特性。在Go中:

  • 函数是值,这意味着它们可以作为值添加到映射中,作为参数传递给其他函数,设置为变量,并从函数返回(称为“高阶函数”,在 Go 中经常使用装饰器创建中间件图案)。
  • 可以创建和自动调用匿名函数。
  • 在其他函数内声明的函数允许闭包(在函数内声明的函数能够访问和修改在外部函数中声明的变量)。在惯用的 Go 中,闭包被广泛使用来限制函数的范围,并设置函数然后在其逻辑中使用的状态。
func StartTimer (name string) func(){
    t := time.Now()
    log.Println(name, "started")
    return func() {
        d := time.Now().Sub(t)
        log.Println(name, "took", d)
    }
}
func RunTimer() {
    stop := StartTimer("My timer")
    defer stop()
    time.Sleep(1 * time.Second)
}

上面是一个闭包的例子。‘StartTimer' 函数返回一个新函数,它通过闭包可以访问在其出生范围内设置的 ‘t' 值。然后,此函数可以将当前时间与“t”的值进行比较,从而创建一个有用的计时器。感谢Mat Ryer的这个例子。

6. Go 有隐式接口

任何阅读过有关SOLID编码和设计模式的文献的人都可能听说过“优先组合胜过继承”的口头禅。简而言之,这表明您应该将业务逻辑分解为不同的接口,而不是依赖于来自父类的属性和逻辑的分层继承。

另一个流行的方法是“为接口编程,而不是实现”: API 应该只发布其预期行为的契约(其方法签名),而不是有关如何实现该行为的详细信息。

这两者都表明接口在现代编程中的重要性。

因此,Go 支持接口也就不足为奇了。事实上,接口是 Go 中唯一的抽象类型。

然而,与其他语言不同,Go 中的接口不是显式实现的,而是隐式实现的。具体类型不声明它实现了接口。相反,如果为该具体类型设置的方法集包含底层接口的所有方法集,则Go 认为该对象实现了 interface。

这种隐式接口实现(正式称为结构类型)允许 Go 强制执行类型安全和解耦,保持动态语言中表现出的大部分灵活性。

相比之下,显式接口将客户端和实现绑定在一起,例如,在 Java 中替换依赖项比在 Go 中困难得多。

// 这是一个接口声明(称为Logic)
type Logic interface {
    Process (data string) string
}

type LogicProvider struct {}
// 这是 LogicProvider 上名为“Process”的方法 struct
 func (lp LogicProvider) Process (data string) string {
    // 业务逻辑
}
// 这是具有 Logic 接口作为属性的客户端结构
type Client struct {
    L Logic
}
func(c Client) Program() {
    // 从某处获取数据
    cLProcess(data)
}
func main() {
    c := Client {
        L: LogicProvider{},
     }
    c.Program()
}

LogicProvider 中没有任何声明表示它符合Logic接口。这意味着客户端将来可以轻松替换其逻辑提供程序,只要该逻辑提供程序包含底层接口 ( Logic ) 的所有方法集。

7.错误处理

Go 中的错误处理方式与其他语言大不相同。简而言之,Go 通过返回一个 error 类型的值作为函数的最后一个返回值来处理错误。

当函数按预期执行时,错误参数返回nil,否则返回错误值。调用函数然后检查错误返回值,并处理错误,或抛出自己的错误。

// 函数返回一个整数和一个错误
func calculateRemainder(numerator int, denominator int) ( int, error ) {
   //
   if denominator == 0 {
      return 9, errors.New("denominator is 0"
    }
   // 没有错误返回
   return numerator / denominator, nil
 }

Go 以这种方式运行是有原因的:它迫使编码人员考虑异常并正确处理它们。传统的 try-catch 异常还会在代码中添加至少一个新的代码路径,并以难以遵循的方式缩进代码。Go 更喜欢将“快乐路径”视为非缩进代码,在“快乐路径”完成之前识别并返回任何错误。

8.并发

可以说是 Go 最著名的特性,并发允许处理在机器或服务器上的可用内核数量上并行运行。当单独的进程不相互依赖(不需要顺序运行)并且时间性能至关重要时,并发性最有意义。这通常是 I/O 要求的情况,其中读取或写入磁盘或网络的速度比除最复杂的内存中进程之外的所有进程慢几个数量级。
函数调用前的“ go ”关键字将同时运行该函数。

func process(val int) int {
   // 用 val 做一些事情
}
// 对于 'in' 中的每个值,同时运行 process 函数,
// 并将 process 的结果读取到 'out'
 func runConcurrently(in <-chan int, out chan<- int){
   go func() {
       for val := range in {
            result := process(val)
            out <- result
       }
   }
}

Go 中的并发是一项深入且相当高级的功能,但在有意义的地方,它提供了一种有效的方法来确保程序的最佳性能。

9. Go 标准库

Go 有一个“包含电池”的理念,现代编程语言的许多要求都被纳入标准库,这使程序员的生活变得更加简单。
如前所述,Go 是一种相对年轻的语言,这意味着标准库中可以满足现代应用程序的许多问题/要求。

一方面,Go 为网络(特别是 HTTP/2)和文件管理提供了世界一流的支持。它还提供原生 JSON 编码和解码。因此,设置服务器来处理 HTTP 请求并返回响应(JSON 或其他)非常简单,这解释了 Go 在基于 REST 的 HTTP Web 服务开发中的流行。

正如Mat Ryer还指出的那样,标准库是开源的,是学习 Go 最佳实践的绝佳方式。

如果你真的从这篇文章中学到了一些新东西,喜欢它,收藏它并与你的小伙伴分享。🤗最后,不要忘了❤或📑支持一下哦

到此这篇关于Golang与其他语言不同的九个特性的文章就介绍到这了,更多相关Golang 特性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • golang1.16新特性速览(推荐)

    golang1.16也在今天正式发布了. 原定计划是2月1号年前发布的,不过迟到也是golang的老传统了,正好也趁着最后的假期快速预览一下golang1.16的新特性吧. 语言內建的资源嵌入支持 之前市面上已经有很多把今天文件嵌入golang二进制程序的工具了,这次golang官方将这一功能加入了embed标准库,从语言层面上提供了支持. 我之前以及写了embed的使用教程,可以看这里. 这儿还有一篇官方推荐的教程. 支持arm64 m1芯片可谓是最近的焦点,golang自然也不会落下. 在g

  • golang中defer的关键特性示例详解

    前言 大家都知道golang的defer关键字,它可以在函数返回前执行一些操作,最常用的就是打开一个资源(例如一个文件.数据库连接等)时就用defer延迟关闭改资源,以免引起内存泄漏.本文主要给大家介绍了关于golang中defer的关键特性,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 一.defer 的作用和执行时机 go 的 defer 语句是用来延迟执行函数的,而且延迟发生在调用函数 return 之后,比如 func a() int { defer b() return

  • Golang与其他语言不同的九个特性

    目录 1. Go 总是在构建中包含二进制文件 2. Go 没有针对程序依赖的集中托管服务 3. Go 是按值调用的 4. 'defer' 关键字 6. Go 有隐式接口 7.错误处理 8.并发 9. Go 标准库 随着编程语言的发展,Go 还很年轻.它于 2009 年 11 月 10 日首次发布.其创建者Robert Griesemer Rob Pike 和 Ken Thompson在 Google 工作,在那里大规模扩展的挑战激励他们将 Go 设计为一种快速有效的编程解决方案,用于具有大型代码

  • golang 调用c语言动态库方式实现

    下面我们自己在 Linux 下做一个动态库(.so 文件 - Shared Object),然在用 Go 来使用它.本文所用的操作系统为 Ubuntu18.04, 以 gcc 作为编译器. 1.实现头文件,声明文件中函数.这里创建一个add.h文件. #ifndef __ADD_H__ #define __ADD_H__ char* Add(char* src, int n); #endif 2.实现add主体函数add.c #include <string.h> #include <s

  • Windows Server2008服务器系统的九大特性

    Windows Server 2008的发布已经指日可待,其第一个candidate版也已经发布,正式版也会在不久之后发布. 上一个服务器版本的Windows还是Windows Server 2003,尽管期间微软发布过Windows Server 2003 R2,但是毕竟不是一个独立的新版本.因此,此次即将发布的Windows Server 2008备受微软重视,与Windows Server 2008相关的重大特性也被媒体反复渲染.其中,有很多特性已经为大多数IT专家们所熟悉,比如有关不带图

  • 在Linux系统中安装Go语言的详细教程

    Go (也叫 "golang")是一款由Google最初开发的编程语言.它自诞生就有几个设计原则:简单性.安全性和速度.Go语言发行版拥有各种调试.测试.调优和代码审查工具.如今Go语言和它的工具链在大多数Linux发行版的基础仓库都可用,用默认的包管理器就可以安装. 在Ubuntu.Debian 或者 Linux Mint上安装Go语言 下面是在基于Debian的发行版上使用apt-get来安装Go语言和它的开发工具. $ sudo apt-get install golang 检查

  • golang实现并发数控制的方法

    golang并发 谈到golang这门语言,很自然的想起了他的的并发goroutine.这也是这门语言引以为豪的功能点.并发处理,在某种程度上,可以提高我们对机器的使用率,提升系统业务处理能力.但是并不是并发量越大越好,太大了,硬件环境就会吃不消,反而会影响到系统整体性能,甚至奔溃.所以,在使用golang提供便捷的goroutine时,既要能够实现开启并发,也要学会如果控制并发量. 开启golang并发 golang开启并发处理非常简单,只需要在调用函数时,在函数前边添加上go关键字即可.如下

  • Go语言 channel如何实现归并排序中的merge函数详解

    前言 初识go语言不到半年,我是一次偶然的机会认识了golang这门语言,看到他简洁的语法风格和强大的语言特性,瞬间有了学习他的兴趣 最近学习 Go,但是苦于没有项目练手,于是便逼迫自己:如果想到什么有趣的东西,看能不能用 Go 实现一遍,于是便有了这篇流水文. 实现过程 归并排序中的 merge 函数,相信每个人都很熟悉,网上随便搜搜都有一大堆文章,这里不再赘述细节.一开始,我用的是常规套路,不过觉得没啥意思,无非是「换汤不换药,感觉还是在拿自己熟悉的语言写东西」. 联想到 Go 的 chan

  • golang实现http server提供文件下载功能

    简介 Go(又称Golang)是Google开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言. 罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及肯·汤普逊(Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor.Russ Cox加入项目.Go是基于Inferno操作系统所开发的.Go于2009年11月正式宣布推出,成为开放源代码项目,并在Linux及Mac OS X平台上进行了实现,后来追加了Windo

  • golang之JWT实现的示例代码

    什么是JSON Web Token? JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON方式安全地传输信息.由于此信息是经过数字签名的,因此可以被验证和信任.可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名. 直白的讲jwt就是一种用户认证(区别于session.cookie)的解决方案. 出现的背景 众所周知,在jwt出现之前,我们已经有session.cookie来解决用户登

  • golang实现各种情况的get请求操作

    请求地址 var ( requestGetURLNoParams string = "http://httpbin.org/get" requestGetURL string = "http://httpbin.org/get?a=a&b=b&c=ccc" imageURL string = "http://httpbin.org/image" ) 普通get请求 // 基本get请求 func basicGet() { resp

  • 通过汇编看golang函数的多返回值问题

    golang这门语言,有个比较好的特性,就是支持函数的多返回值.想C,C++,Java等这些语言,是不支持函数多返回的.但是C,C++可以使用传递指针,实现函数多返回.但是,你有没有想过,golang是怎样实现函数多返回值的呢? 我们知道,C,C++是通过寄存器实现函数返回值的,也就是先把返回值写入到一个寄存器中,然后再从寄存器中,读到函数的返回值.golang也是这样实现的吗? 伟大的思想家孔子曾说过,在源码面前一切都如同裸奔.后来,鲁迅先生,总结了孔子的思想,说出了,在汇编面前,一切语法都是

随机推荐