这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
一. Go历史
1.诞生时间
Go 语言起源 2007 年,并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。
-
2007 年 9 月 20 日的下午,在谷歌山景城总部的一间办公室里,谷歌的大佬级程序员 Rob Pike在等待一个 C++ 项目构建的过程中和谷歌的另外两个大佬级程序员 Robert Griesemer和 Ken Thompson进行了一次有关设计一门新编程语言的讨论。
- 计算机硬件技术更新频繁,性能提高很快。目前主流的编程语言发展明显落后于硬件,不能合理利用多核多CPU的优势提升软件系统性能。
- 软件系统复杂度越来越高,维护成本越来越高,目前缺乏一个足够简洁高效的编程语言。
- 企业运行维护很多c/c++的项目,C/C++程序运行速度虽然很快(因为采用静态编译),但是编译速度却很慢,同时还存在内存泄露的一系列困扰需要解决。
2. 为什么使用Go
- 简单好记的关键词和语法。轻松上手,简单易学。
- 更高的效率。比Java,C++等拥有更高的编译速度,同时运行效率媲美C,同时开发效率非常高。
- 生态强大,网络上库很丰富,很多功能使用Go开发非常简单。
- 语法检查严格,高安全性。
- 严格的依赖管理,go mod命令。
- Go拥有强大的编译检查、严格的编码规范和完整的软件生命周期工具,具有很强的稳定性,稳定压倒一切。
- 跨平台交叉编译,windows就可以编译出mac,linux上可执行的程序。
- 异步编程复杂度低,易维护,GO 语言中 Channel 设计,异步程序写起来非常自然。
- 语言层面支持并发,
go关键字(协程)使得go的并发效率极高。 - 严格的语法规范,所有程序员写出来的代码都是一样的,对大团队来说,非常友好。
- Go 的并发、性能、安全性、易于部署等特性,使它很容易成为“云原生语言”。容器和云的使用上非常广
二。GO基础语法
1.Hello world
第一行 package main代表这个文件属于main包的一部分,main包也就是程序的入口包。第三行导入了标准库里的FMT包,这个包主要是用来往屏幕输入和输出字符串、格式化字符串。
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
2.变量
go语言是一门强类型语言,每一个变量都有它自己的变量类型。常见的变量类型包括字符串、整数、浮点型、布尔型等。go语言的字符串是内置类型,可以直接通过加号拼接,也能够直接用等于号去比较两个字符串。在go语言里面,大部分运算符的使用和优先级都和C或者C++类似,这里就不再概述。
package main
import (
"fmt"
"math"
)
func main() {
var a = "initial"
var b, c int = 1, 2
var d = true
var e float64
f := float32(e)
g := a + "foo"
fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
fmt.Println(g) // initialapple
const s string = "constant"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
3.if else
go语言里的if else写法和C或者C++类似。第一个不同点是if后面没有括号。如果你写了括号的话,那么在保存的时候你的编辑器会自动把你去掉。第二个不同点是Golang里面的if,它后面必须接大括号,不能像C或者C++一样,直接把if里面的语句同一行。
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
4.循环
在go里面没有while循环、do while循环,只有唯一的一种for循环。最简单的for循环就是在for后面什么都不写,代表一个死循环。循环途中你可以用break跳出,也可以使用经典的c循环,在循环里面,你可以用break或者continue来跳出或者继续循环。
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println("loop")
break
}
for j := 7; j < 9; j++ {
fmt.Println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}
}
5.switch
go语言里的switch分支结构,看起来也和C或者C++比较类似。但有一个很大的不同是,在c++里面,switch case 如果不加break的话会继续往下跑完所有case,而在go语言里是不用加break的。相比C或者C++,go语言里的break更加强大。可以使用任意的变量类型,甚至可以用来取代任意的if else 语句。你可以在switch后面不加任何的变量,然后在case里面写条件分支。这样代码会更加清晰
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
6.数组
数组就是一个具有编号且长度固定的元素序列。对于一个数组,可以很方便地取特定索引的值或者往特定索引存储值。不过,在真实业务代码里面,我们很少直接使用数组,因为他的长度是固定的,我们用的更多的是切片。
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println("get:", a[2])
fmt.Println("len:", len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
7.切片
切片不同于数组,它可以任意更改长度,使之有更多丰富的操作。比如我们可以用make来创建一个切片,可以像数组一样去取值,使用append来追加元素。注意append的用法的话,你必须把append的结果赋值为原数组。因为slice的原理实际上是它存储了一个长度和一个容量,加一个指向一个数组的指针,在你执行append操作的时候,如果容量不够的话,会扩容并返回新的slice。slice初始化时也可以指定长度。
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s) // [a b c d e f]
c := make([]string, len(s))
copy(c, s)
fmt.Println(c) // [a b c d e f]
fmt.Println(s[2:5]) // [c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[2:]) // [c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) // [g o o d]
}