简述
Go 语言是一门静态类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go 语言的语法接近 C 语言,支持垃圾回收,同时也支持面向对象编程范式,
但在语法上并不支持类和继承等面向对象的概念,可以通过结构体和方法来实现类似的功能。
1. 变量
Go 语言中的变量声明格式为:var 变量名 变量类型
var a int
var b string
var c bool
变量声明和初始化也可以使用一种更为简短的模式,变量名 := 表达式,Go语言支持自动推断类型
a := 1
b := "hello"
c := true
需要注意的是,在Go语言中,变量声明后必须要使用,否则会编译不通过。
如果我们只是想声明一个变量,但暂时不想使用它,可以使用下划线符号_来表示该变量不被使用
var _ int
2. 常量
Go 语言中使用const关键字来声明常量
const a int = 1
const b string = "hello"
3. 流程控制
3.1 条件语句
Go 语言中的条件语句有if和switch两种
3.1.1 if
if a > 0 {
fmt.Println("a > 0")
} else if a < 0 {
fmt.Println("a < 0")
} else {
fmt.Println("a = 0")
}
功能上和 C 语言中的if语句类似,
只是语法上略有不同,比如if后的条件不需要使用括号包裹
3.1.2 switch
switch a {
case 1:
fmt.Println("a = 1")
case 2:
fmt.Println("a = 2")
default:
fmt.Println("a != 1 && a != 2")
}
3.2 循环语句
3.2.1 for
Go 语言中的循环语句只有for和range两种
for i := 0; i < 10; i++ {
fmt.Println(i)
}
其中,for的三个语句都是可选的,可以省略其中的任意一个或多个,
类似于 C 语言中的while(true)可以写成:
for {
fmt.Println("hello")
}
3.2.2 range
Go 语言中的range关键字可以用来遍历
数组、切片、字符串、map等数据结构
for i, v := range arr {
fmt.Println(i, v)
}
例如上例中,i表示数组的下标,v表示数组的值
3.3 break 和 continue
和 C 语言中的用法一致
4. 函数
4.1 函数声明
Go 语言中的函数使用func关键字来声明
func add(a, b int) int {
return a + b
}
其中,a和b为形参,int是函数的返回值类型
4.2 函数返回
比较有特点的是,Go 语言中的函数可以返回多个值
func swap(a, b int) (int, int) {
return b, a
}
在调用时,可以同时接受两个返回,也可以使用下划线符号_来表示不需要的返回值
a, b := swap(1, 2)
_, b := swap(1, 2)
5. 指针
Go 语言中的指针和 C 语言中的指针类似, 但是不及C语言中的指针灵活,Go语言中的指针不能进行运算
a := 1
b := &a
fmt.Println(*b) // 1
6. 结构体
Go 语言中的结构体和 C 语言中的结构体类似, 但是不同的是,Go语言中的结构体可以包含方法,也就是更接近于类的概念
type Person struct {
name string
age int
}
func (p Person) say() {
fmt.Println("hello")
}
其中,Person为结构体名,name和age为结构体的成员,还包含了一个say方法
7. 接口
作为补充,Go语言中也存在接口的概念,
type Shape interface {
Area() float64
Perimeter() float64
}
其中,Shape为接口名,Area和Perimeter为接口的方法,
我们的结构体可以通过实现接口的方法来实现接口
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
由于Rectangle结构体实现了Shape接口中的所有方法,因此它被认为是Shape接口的实现类型。
8. 并发
8.1 goroutine
Go 语言中的并发使用goroutine来实现,goroutine即协程,
相较于线程,协程的开销更小,可以同时运行成千上万个协程。
func main() {
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
time.Sleep(time.Second)
}
在上面的例子中,我们使用go关键字来开启一个协程,
使用for循环开启了10个协程,每个协程都会打印出自己的编号,
使用time.Sleep来等待协程执行完毕。
8.2 channel
Go 语言中的协程之间通信使用channel来实现,channel即管道,
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
}
在上面的例子中,我们使用make函数来创建了一个channel,
然后在协程中向channel中写入了一个值,最后在主协程中读取了这个值。
9. 包
Go 语言中的包使用package关键字来声明,一个包中可以包含多个文件,
但是一个文件只能属于一个包。
包的导入使用import关键字来实现,导入包之后,可以使用包中的函数和变量。
比如我们最早使用的fmt包,就是Go语言中的标准库。
import "fmt"
func main() {
fmt.Println("hello")
}
10. 错误处理
Go 语言中的错误处理使用error类型来实现,error类型是一个接口类型,
只要实现了Error() string方法的类型都可以作为error类型使用。
type error interface {
Error() string
}
在Go语言中,我们可以通过errors.New函数来创建一个error类型的值
err := errors.New("hello")
由于Go支持多返回值,因此我们可以在函数中返回一个error类型的值
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("div by zero")
}
return a / b, nil
}
这样,我们就可以在调用函数时,判断函数是否返回了error类型的值
,来判断函数是否执行成功
if v, err := div(1, 0); err != nil {
fmt.Println(err)
} else {
fmt.Println(v)
}
11. defer
defer常被用于处理资源释放的问题
func main() {
f, err := os.Open("test.txt")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
}
在执行中,多个defer语句的执行顺序是先进后出的
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// 3
// 2
// 1
}