Skip to content

简单认识golang语言(为云而生的现代语言)

参考:https://learnku.com/docs/the-way-to-go/chapter-description/3598https://wizardforcel.gitbooks.io/qt-beginning/content/1.html

1、起源

  • 出生名门
    • 诞生于google公司
    • Ken Thompson:Unix操作系统和B语言(C语言的前身)的创造者、UTF-8编码的发明者
    • Rob Pike:Unix项目的参与者、UTF-8编码的联合创始人和Limbo编程语言(Go语言的前身)的创造者
    • Robert Griesemer:著名Javascript引擎V8的创造者
  • 语法、数据类型、控制流等继承自C
  • 包特性、面向对象特性源于Pascal
  • 基于管道通信的协程并发模型借鉴于CSP Go 语言起源 2007 年,并于 2009 年正式对外发布。

官网: golang.org github: github.com/golang/go

执行速度快但是编译速度并不理想的语言(如:C++),编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言呢?显然,Go 语言在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。

Go 语言是一门类型安全和内存安全的编程语言。虽然 Go 语言中仍有指针的存在,但并不允许进行指针运算。

2、Go语言主要用来做哪些场景开发

Go语言是一种开源的编程语言,它的设计目标是提供一种简单、高效、可靠的编程语言。Go语言主要用于以下场景开发:

网络编程:Go语言的并发模型和轻量级线程(goroutine)机制使其非常适合网络编程,可以轻松地实现高并发的网络应用。 分布式系统:Go语言的标准库提供了丰富的支持,可以方便地实现分布式系统中的各种组件,如RPC、消息队列等。 Web开发:Go语言的高效性和简洁性使其成为Web开发的理想选择,可以使用各种框架(如Beego、Gin等)来快速构建Web应用程序。 数据库编程:Go语言提供了对各种数据库的支持,包括MySQL、PostgreSQL、MongoDB等,可以方便地进行数据库编程。 系统编程:Go语言的底层支持和跨平台性使其非常适合系统编程,可以方便地实现各种系统级应用程序。

总之,Go语言是一种非常灵活的编程语言,可以用于各种场景的开发。

3、安装和运行环境

目前有 2 个版本的编译器:Go 原生编译器 gc 和非原生编译器 gccgo,这两款编译器都是在类 Unix 系统下工作 。

windows平台一键安装go1.24.5.windows-amd64.msi后,即可直接cmd窗口go run helloworld.go。(没有安装其他编译器) 注意:官网需要翻墙才能访问,特意花了5.7元包月机场。

4、格式化代码

在命令行输入 gofmt –w program.go 会格式化该源文件的代码然后将格式化后的代码覆盖原始内容(如果不加参数 -w 则只会打印格式化后的结果而不重写文件);gofmt -w *.go 会格式化并重写所有 Go 源文件;gofmt map1 会格式化并重写 map1 目录及其子目录下的所有 Go 源文件。

gofmt 也可以通过在参数 -r 后面加入用双引号括起来的替换规则实现代码的简单重构,规则的格式:<原始内容> -> <替换内容>。

5、生成代码文档

go doc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档。

6、其他工具

go install 是安装 Go 包的工具,类似 Ruby 中的 rubygems。主要用于安装非标准库的包文件,将源代码编译成对象文件。

go fix 用于将你的 Go 代码从旧的发行版迁移到最新的发行版,它主要负责简单的、重复的、枯燥无味的修改工作,如果像 API 等复杂的函数修改,工具则会给出文件名和代码行数的提示以便让开发人员快速定位并升级代码。Go 开发团队一般也使用这个工具升级 Go 内置工具以及 谷歌内部项目的代码。go fix 之所以能够正常工作是因为 Go 在标准库就提供生成抽象语法树和通过抽象语法树对代码进行还原的功能。该工具会尝试更新当前目录下的所有 Go 源文件,并在完成代码更新后在控制台输出相关的文件名称。

go test 是一个轻量级的单元测试框架。

7、与 C/c++ 进行交互

cgo 会替代 Go 编译器来产生可以组合在同一个包中的 Go 和 C 代码。在实际开发中一般使用 cgo 创建单独的 C 代码包。

如果你想要在你的 Go 程序中使用 cgo,则必须在单独的一行使用 import "C" 来导入,一般来说你可能还需要 import "unsafe"。

然后,你可以在 import "C" 之前使用注释(单行或多行注释均可)的形式导入 C 语言库(甚至有效的 C 语言代码),它们之间没有空行。

8、文件名、关键字与标识符

文件名只能是小写字母或_,后缀为.go。

9、包

每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。

你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main 表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。

import "fmt"
import "os"

import "fmt"; import "os"

import (
   "fmt"
   "os"
)

import ("fmt"; "os")

当你导入多个包时,最好按照字母顺序排列包名,这样做更加清晰易读。 使用包的别名来解决包名之间的名称冲突,或者说根据你的个人喜好对包名进行重新设置,如:import fm "fmt"。

10、函数

main 函数既没有参数,也没有返回类型(与 C 家族中的其它语言恰好相反)。如果你不小心为 main 函数添加了参数或者返回类型,将会引发构建错误

大括号规则: 左大括号 { 必须与方法的声明放在同一行,这是编译器的强制规定。 这是因为编译器会产生 func main() ; 这样的结果,很明显这错误的) Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误。 对于大括号 {} 的使用规则在任何时候都是相同的(如:if 语句等)。

函数名命名规则: 只有当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。

11、注释

和c/c++语言一样。 注释不会被编译,但可以通过 godoc 来使用。

每一个包应该有相关注释,在 package 语句之前的块注释将被默认认为是这个包的文档说明,其中应该提供一些相关信息并对整体功能做简要的介绍。一个包可以分散在多个文件中,但是只需要在其中一个进行注释说明即可。

几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。如果这种注释(称为文档注释)出现在函数前面,例如函数 Abcd,则要以 "Abcd..." 作为开头。

12、类型

结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是 NULL 或 0)。值得注意的是,Go 语言中不存在类型继承。

函数也可以是一个确定的类型,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后。

type (
   IZ int
   FZ float64
   STR string
)

13、Go程序的一般结构

package main

import (
   "fmt"
)

const c = "C"

var v int = 5

type T struct{}

func init() { // initialization of package
}

func main() {
   var a int
   Func1()
   // ...
   fmt.Println(a)
}

func (t T) Method1() {
   //...
}

func Func1() { // exported function Func1
   //...
}

14、命名规范

名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符。返回某个对象的函数或方法的名称一般都是使用名词,没有 Get... 之类的字符,如果是用于修改某个对象,则使用 SetName。有必须要的话可以使用大小写混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下划线来分割多个名称。

15、常量

常量的值必须是能够在编译时就能够确定的;你可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。

正确的做法:const c1 = 2/3 错误的做法:const c2 = getNumber() // 引发构建错误: getNumber() used as value 因为在编译期间自定义函数均属于未知,因此无法用于常量的赋值,但内置函数可以使用,如:len ()。

iota 也可以用在表达式中,如:iota + 50。在每遇到一个新的常量块或单个常量声明时, iota 都会重置为 0( 简单地讲,每遇到一次 const 关键字,iota 就重置为 0 )。

16、赋值

=声明包级别的全局变量,当你在函数体内声明局部变量时,应使用简短声明语法 :=。 只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。 如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,但是使用=号是可以的。

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,但是全局变量是允许声明但不使用。

17、打印

%s 代表字符串标识符、%v 代表使用类型的默认输出格式的标识符。 在格式化字符串里,%d 用于格式化整数(%x 和 %X 用于格式化 16 进制表示的数字),%g 用于格式化浮点型(%f 输出浮点数,%e 输出科学计数表示法),%0d 用于规定输出定长的整数,其中开头的数字 0 是必须的。

%n.mg 用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f,例如:使用格式化字符串 %5.2e 来输出 3.4 的结果为 3.40e+00。

print: 是 Go 的内置函数,无需导入包即可使用。但它仅在 Go 的编译和运行时内部使用,不推荐在正式代码中使用。 功能简单,仅用于调试,输出格式可能因环境而异(如不保证换行、不处理复杂类型)。 不支持格式化占位符(如 %d、%s)。

fmt.Print: 是 fmt 包中的函数,需通过 import "fmt" 导入后使用。它是标准库提供的公开接口,用于格式化输出,是正式代码中的首选。

18、init 函数

变量除了可以在全局声明中初始化,也可以在 init 函数中初始化。这是一类非常特殊的函数,它不能够被人为调用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。

每个源文件都只能包含一个 init 函数。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。

一个可能的用途是在开始执行程序之前对数据进行检验或修复,以保证程序状态的正确性。

Go 在模块模式下不支持相对导入路径导致的。从 Go 1.16 开始,默认启用模块模式,此时必须使用模块路径进行导入。 如果只是临时测试,可通过设置环境变量禁用模块模式:

go env -w GO111MODULE=off

19、数字类型

Go 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用。

20、随机数

函数 rand.Float32 和 rand.Float64 返回介于 [0.0, 1.0) 之间的伪随机数,其中包括 0.0 但不包括 1.0。函数 rand.Intn 返回介于 [0, n) 之间的伪随机数。

你可以使用 Seed(value) 函数来提供伪随机数的生成种子,一般情况下都会使用当前时间的纳秒级数字。

21、运算符和优先级

优先级 运算符 7 ^ ! 6 * / % << >> & &^ 5 + - | ^ 4 == != < <= >= > 3 <- 2 && 1 ||

22、rune

rune实际上是int32的别名,其主要作用是表示 Unicode 码点。Unicode 涵盖了全球各种语言的字符,每个字符都对应着唯一的码点。借助rune类型,在处理包含非 ASCII 字符(像中文、日文、 emoji 等)的字符串时会更加便捷。

在 Go 语言中,字符串的底层存储形式是 UTF-8 编码的字节序列。若想遍历字符串中的 Unicode 字符,就需要将字符串转换为rune切片。

23、指针

你不能得到一个文字或常量的地址。

Go 语言和 C、C++ 以及 D 语言这些低层(系统)语言一样,都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算(所谓的指针算法,如:pointer+2,移动指针指向字符串的字节数或数组的某个位置)是不被允许的。Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。

因此 c = *p++ 在 Go 语言的代码中是不合法的。

指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。

24、for结构

for 结构的第二种形式是没有头部的条件判断迭代(类似其它语言中的 while 循环),基本形式为:for 条件语句 {}。

条件语句是可以被省略的,如 i:=0; ; i++ 或 for { } 或 for ;; { }(;; 会在使用 gofmt 时被移除):这些循环的本质就是无限循环。最后一个形式也可以被改写为 for true { },但一般情况下都会直接写 for { }。

25、标签和goto

标签的名称是大小写敏感的,为了提升可读性,一般建议使用全部大写字母。

26、函数

除了 main ()、init () 函数外,其它所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。

函数重载(function overloading)指的是可以编写多个同名函数,只要它们拥有不同的形参 / 或者不同的返回值,在 Go 里面函数重载是不被允许的。 Go 语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能;没有重载意味着只是一个简单的函数调度。

没有参数的函数通常被称为 niladic 函数(niladic function),就像 main.main()。

27、变长参数

如果参数被存储在一个 slice 类型的变量 slice 中,则可以通过 slice... 的形式来传递参数调用变参函数。

28、defer和追踪

关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。

29、闭包

匿名函数同样被称之为闭包(函数式语言的术语):它们被允许调用定义在其它环境下的变量。闭包可使得某个函数捕捉到一些外部状态,例如:函数被创建时的状态。另一种表示方式为:一个闭包继承了函数所声明时的作用域。这种状态(作用域内的变量)都被共享到闭包的环境中,因此这些变量可以在闭包中被操作,直到被销毁。闭包经常被用作包装函数:它们会预先定义好 1 个或多个参数以用于包装,详见下一节中的示例。另一个不错的应用就是使用闭包来完成更加简洁的错误检查。

一个返回值为另一个函数的函数可以被称之为工厂函数,这在您需要创建一系列相似的函数的时候非常有用:书写一个工厂函数而不是针对每种情况都书写一个函数。

可以返回其它函数的函数和接受其它函数作为参数的函数均被称之为高阶函数,是函数式语言的特点。

30、函数签名

在编程中,函数签名(Function Signature) 是描述函数接口的元数据集合,它定义了函数的调用方式和参数约定。理解函数签名对于正确使用函数、实现多态以及代码分析至关重要。

函数签名通常包含:

  • 函数名称:标识函数的唯一标识符(如 add、getUserInfo)。
  • 参数列表:参数的类型、顺序和数量(如 (int a, int b))。
  • 返回类型(部分语言):函数返回值的类型(如 int、void)。
  • 参数修饰符(如 const、ref、泛型类型参数等)。

31、切片

切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。

优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。

32、new () 和 make () 的区别

看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。

new (T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{}。

make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel。 换言之,new 函数分配内存,make 函数初始化。

This website is released under the MIT License.