Go语言区别于其他语言的特性

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

前言:

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

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

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

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

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

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

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

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

3. Go 是按值调用的

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

JavaPython 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 最佳实践的绝佳方式。

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

(0)

相关推荐

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

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

  • MongoDB的基本特性与内部构造的讲解

    MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型.Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引. 对于大多数的MongoDB的用户来说,MongoDB就像是一个大黑盒.但是如果你能够了解到MongoDB一些内部构造的话,将有利于你更好地理

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

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

  • django 中QuerySet特性功能详解

    Book表的数据显示 id title price publish_id 2 Linux 30 1 3 项塔兰 45 2 4 追风筝的人 39.9 3 5 富爸爸 23 10 创建queryset 视图 函数, 可以使用的列表的方法,按索引,切片的方法取值,得到一个列表对象 def queryset(request): ret1 = Book.objects.all()[0] #QuerySet支持索引,切片操作 # print(ret1) #linux 得到一个具体的对象 ret2 = Boo

  • go特性之数组与切片的问题

    数组: 复制传递(不要按照c/c++的方式去理解,c/c++中数组是引用传递),定长 切片: 引用传递,底层实现是3个字段 array(数组) + len(长度) +cap(容量) go/src/runtime/slice.go slice结构定义: type slice struct { array unsafe.Pointer len int cap int } 要特别注意的是,切片的引用传递指的是切片传递时,切片的array字段是引用传递的,len和cap字段依然是赋值传递. 写个伪代码:

  • 分析MongoDB和MySQL各自的关键特性、差别和优势

    目录 什么是 MySQL? 什么是 MongoDB? MongoDB 与 MySQL 的差异 数据模式和容量 性能和速度 安全性 事务的特性:原子性.一致性.隔离性和持久性 查询 MongoDB vs. MySQL:分别在什么情况下使用 结论 MongoDB 和 MySQL 都是不错的数据库,都具有优良的性能.然而,它们是否成功取决于应用场景.首先应当了解它们各自不同的运行环境,而不能只比较各自的优点和缺点.因此,在本文中,我们将探讨 MongoDB 和 MySQL 各自的关键特性.差别和优势.

  • Django 2.0版本的新特性抢先看!

    前言 2017年12月2日,Django官方发布了2.0版本,成为多年来的第一次大版本提升,那么2.0对广大Django使用者有哪些变化和需要注意的地方呢? 一.Python兼容性 Django 2.0支持Python3.4.3.5和3.6.Django官方强烈推荐每个系列的最新版本. 最重要的是Django 2.0不再支持Python2! Django 1.11.x是支持Python2.7的最后版本. 二.2.0新特性 1.简化了URL路由语法 django.urls.path()方法的语法更

  • Go语言区别于其他语言的特性

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

  • Python代码显得Pythonic(区别于其他语言的写法)

    目录 一.字符串 二.运算 三.赋值 四.变量命名 五.提升代码可读性 (一)避免用分号;在一行内些多条语句 (二)避免用\符号来换行 (三)if-else三元表达式 一.字符串 对于字符串连接,相比于简单的+,更pythonic的做法是尽量使用%操作符或者format函数格式化字符串. name = "jack" n = 90   # no x = 'name: ' + name + '; score: ' + str(n)      # yes  x = 'name: %s; sc

  • C 语言基础之C语言的常见关键字

    目录 ​1.auto 2.register 3.signed和unsigned 4.typedef 5.extern 6.拓展 首先我们简单的和这些关键字见见面(被高亮的关键字是今天要介绍的) 这其中有大家熟知的数据类型:int,char,float,double- 也有控制语句用到的:if,for,do- 还有一些就是今天主要介绍的关键字. 至于还有一些新增的关键字,以上表格未曾提到,大家如果想去了解,可自行查找. 个别术语介绍(可先跳过,后文如若遇到不懂,可回来了解) 自动变量:指的是局部作

  • CodeIgniter采用config控制的多语言实现根据浏览器语言自动转换功能

    本文以实例讲述了CodeIgniter采用config控制的多语言实现根据浏览器语言自动转换功能,对于网站开发来说非常实用. 具体操作方法如下: 语言包文件如下: application\language\english\bm_lang.php application\language\zh-cn\bm_lang.php 注意: 1. "_lang.php"的前缀要一致: 2. 如果还有其他语言包,可创建文件application\language\***\bm_lang.php(**

  • Go语言中嵌入C语言的方法

    本文实例讲述了Go语言中嵌入C语言的方法.分享给大家供大家参考.具体分析如下: Go语言官方带了一个工具叫cgo,可以很方便的在Go语言代码中内嵌C代码或做C和Go代码的集成.下面是一段简单的在Go中内嵌C的实验代码: 复制代码 代码如下: package main /* #include <stdio.h> #include <stdlib.h> void say_hello() {         printf("Hello World!\n"); } */

  • 动态语言、动态类型语言、静态类型语言、强类型语言、弱类型语言介绍

    关于如题这几个概念,大部分人应该比较熟悉,但是我昏了好久,所以写下来加深印象. 1. 动态语言 又叫动态编程语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化.比如众所周知的ECMAScript(JavaScript)便是一个动态语言.除此之外如Ruby.Python等也都属于动态语言,而C.C++等语言则不属于动态语言. 而其实我们平时说的"动态语言"应该指的是"动态类型语言".至少我之前一直是这么认为的. 2. 动态类

  • R语言是什么 R语言简介

    R是由Ross Ihaka和Robert Gentleman在1993年开发的一种编程语言,R拥有广泛的统计和图形方法目录.它包括机器学习算法.线性回归.时间序列.统计推理等.大多数R库都是用R编写的,但是对于繁重的计算任务,最好使用C.c++和Fortran代码. R不仅在学术界很受欢迎,很多大公司也使用R编程语言,包括Uber.谷歌.Airbnb.Facebook等.用R进行数据分析需要一系列步骤:编程.转换.发现.建模和交流结果 R 语言是为数学研究工作者设计的一种数学编程语言,主要用于统

  • C 语言基础之C 语言三大语句注意事项

    目录 1.分支语句 2.if语句 3.switch语句 3.1语句结构 4.循环语句 4.1 while循环(do while类似) 4.2 do while循环 4.3 for循环 5.goto语句 在今天的内容介绍之前我们要知道:C语言中,由一个分号( ; )隔开的就是一条语句. 很好理解,如: int a=3;//语句1 printf("请大家多多指教!");//语句2 ;//语句3----空语句 今天讲解的内容,则是自己对于这三种语句一些细节的介绍.(并不是具体讲解这些语句)

  • 整理C语言中各种类型指针的特性与用法

    指针为什么要区分类型: 在同一种编译器环境下,一个指针变量所占用的内存空间是固定的.比如,在16位编译器环境 下,任何一个指针变量都只占用8个字节,并不会随所指向变量的类型而改变. 虽然所有的指针都只占8个字节,但不同类型的变量却占不同的字节数. 一个int占用4个字节,一个char占用1个字节,而一个double占用8字节: 现在只有一个地址,我怎么才能知道要从这个地址开始向后访问多少个字节的存储空间呢,是4个,是1个,还是8个. 所以指针变量需要它所指向的数据类型告诉它要访问多少个字节存储空

  • 我为什么喜欢Go语言(简洁的Go语言)

    从2000年至今,也写了11年代码了,期间用过VB.Delphi.C#.C++.Ruby.Python,一直在寻找一门符合自己心意和理念的语言.我很在意写代码时的手感和执行的效率,所以在Go出现之前一直没有找到.在熟悉Go之后,我虽没有停下脚步,也去体验了D语言,但几乎立即就放弃了,它的设计还是太复杂. 就说说Go吧.它的好其实也就两个字--简洁! 看很多朋友的留言都觉得这些"少个括号.少个分号"之类的东西没什么意义,真的吗?问题是,既然可以没有,为什么非得有?既然能够少打一个字符,为

随机推荐