menu E4b9a6's blog
rss_feed
E4b9a6's blog
有善始者实繁,能克终者盖寡。

Go语言入门指南

作者:E4b9a6, 创建:2019-12-28, 字数:6341, 已阅:27, 最后更新:2019-12-28

这篇文章更新于 1791 天前,文中部分信息可能失效,请自行甄别无效内容。

1. Go

Go(通常称为 Golang)是一种开源编程语言,由 Google 开发,设计目标是简单、高效和可并发,由 Robert Griesemer、Rob Pike 和 Ken Thompson 于 2007 年开始开发,并于 2009 年正式发布 Go常用于网络、系统、并发、分布式编程,采用Go的大型项目包括了 Docker、Kubernetes、Prometheus、Terraform 等,Go语言的主要特性是简洁、并发支持、静态类型、垃圾回收、工具链

2. 入门

2.1. 变量

GO的基础类型包括 BoolStringint/int8/int16/int32/int64uint/uint8(byte)/uint16/uint32/uint64float32/float64complex64/complex128等,变量的命名规则遵循骆驼命名法,如 userName

float32精确到小数点后7位,float64精确到小数点后64位,所以使用==和!=比较float类型时应注意

变量声明:

Go
// 单变量声明
var userName string = "chancel"

// 多个变量声明
var (
    userName string = "chancel"
    userAge int = 10
    userHistory = []float32 {0.01,0.2}
    getName func() string
    userFamily struct {
        fatherName string
    }
)

Go语言有匿名变量,通常采用关键字_作为标识,_变量不占用内存空间也不分配内存

Go
package main

import (
    "fmt"
)

func getData() (int, int) {
    return 1, 2
}

func main() {
    var a, _ = getData()
    fmt.Println("a value is ", a)
}

2.2. 指针运算符

指针运算符是Go语言被归入C语言家族的重要原因,一个指针是指向一个变量值的内存地址变量

一个内存地址用一个操作系统原生字(native word)来存储, 一个原生字在32位操作系统上占4个字节,在64位操作系统上占8个字节

在Go里,一个指针的形式为*T,类型T被成为指针类型的base type,指针通常缩写为Ptr

指针的声明如下,通常采用前面一种(无名指针类型)

Go
*int

type Ptr *int

指针的用途,下面这个例子非常好

Go
package main

import "fmt"

func double(x *int) {
    *x += *x
    x = nil 
}

func main() {
    var a = 3
    double(&a)
    fmt.Println(a) // 6
    p := &a
    double(p)
    fmt.Println(a, p == nil) // 12 false
}

Go语言对指针做了很多限制,例子如下

Go
package main

import "fmt"

func main() {
    a := int64(5)
    p := &a

    // 下面这两行编译不通过	/*
    p++
    p = (&a) + 8
    */

    *p++
    fmt.Println(*p, a)   // 6 6
    fmt.Println(p == &a) // true

    *&a++
    *&*&a++
    **&p++
    *&*p++
    fmt.Println(*p, a) // 10 10
}

2.3. 数组

在Go中,普通数组是固定长度,无法动态添加元素的

声明一个没有初始值数组 array_1 和一个拥有初始值的数组 array_2代码如下

Go
package main

import "fmt"

func main() {
    var array_1 [5]int
    fmt.Println(array_1)

    var array_2 =[5]int{1,2,3,4,5}
    for k,v := range array_2{
    	fmt.Println("Key:",k,"Value:",v)
    }
}

// 输出如下
 go run main.go
[0 0 0 0 0]
Key: 0 Value: 1
Key: 1 Value: 2
Key: 2 Value: 3
Key: 3 Value: 4
Key: 4 Value: 5

固定数组的使用场景较少,我们经常需要更改数组的内容,即可变数组

可变数组的用途非常广泛,在Go中可以借助 Slice(切片) 来实现可变数组

切片的常见使用如下

Go
package main

import (
    "fmt"
    "sort"
)

func main() {
    var array_1 [5]int
    fmt.Println(array_1)

    var array_2 =[5]int{3,5,7,8,1}
    for k,v := range array_2{
    	fmt.Println("Key:",k,"Value:",v)
    }
    
    // 将数组arrya_2转换为切片
    var slice_1 = array_2[:]
    // 注意切片取范围是半开区间,前闭后开,与Python类似
    fmt.Println(slice_1[1:4])

    // 追加元素,注意cap和len的长度变化导致的指针变化
    fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])
    slice_1 = append(slice_1,4,10,2)
    fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])
    slice_1 = append(slice_1,9,6)
    fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])

    // 对元素进行排序
    fmt.Println("slice content:",slice_1)
    sort.Ints(slice_1)
    fmt.Println("slice content:",slice_1)
}

// 输出如下
  go-algorithm-demo go run main.go
[0 0 0 0 0]
Key: 0 Value: 3
Key: 1 Value: 5
Key: 2 Value: 7
Key: 3 Value: 8
Key: 4 Value: 1
[5 7 8]
slice length: 5 cap: 5 ptr: 0xc0000a2090
slice length: 8 cap: 10 ptr: 0xc0000b6000
slice length: 10 cap: 10 ptr: 0xc0000b6000
slice content: [3 5 7 8 1 4 10 2 9 6]
slice content: [1 2 3 4 5 6 7 8 9 10]

可以从输出中看到,当cap变化的时候,切片的内存地址就会发生改变

切片的cap与length的区别在于cap是空间长度,如添加元素时cap长度不足则Go会采用以下公式来进行扩容

Go
var newSclice = make(int[],len(oldSlice),2*len(oldSlice)+1)

2.4. map

Go语言的字典是哈希结构的,key的类型有限制,必须是可以直接比较的类型(Slice/Func/map等不行),Vlaue的类型则没有任何限制

借助value类型为slice的字典可实现Python语言的dict类型的效果

常见使用如下,与其他语言的字典区别并不太大

Go
package main
import "fmt"
func main() {
    var map_1 = map[int]string {1:"liming",2:"zhangsan"}
    fmt.Println(map_1)

    // map可以自动扩容
    map_1[0] = "zijia"
    map_1[3] = "momota"
    map_1[4] = "lindan"

    // map遍历
    for k,v := range map_1{
    	fmt.Println("key",k,"value",v)
    }


    // map查找
    var value_1,isExist_1 = map_1[4]
    fmt.Println("index 4 vlaue exist is",isExist_1,",value is",value_1)

    // 删除元素
    delete(map_1,4)
    var value_2,isExist_2 = map_1[4]
    fmt.Println("index 4 vlaue exist is",isExist_2,",value is",value_2)
}

3. GO语法

3.1. 接口

Go的接口是支持面向对象编程(OOP)的重要标志,与其他语言的接口不一样的是

  1. 类型不需要显式声明它实现了某个接口,多个类型可以实现同一个接口
  2. 只要类型实现了接口中的方法,它就实现了此接口

接口的语法代码如下

Go
//接口定义
type Shape interface {
    Area() float64 //求面积
}
//圆形定义
type Circle struct {
    radius float64
}
//圆形面积计算
func (this Circle) Area() float64 {
    return math.Pi * this.radius * this.radius
}
//矩形定义
type Rectangle struct {
    length  float64
    width float64
}
//矩形面积计算
func (this Rectangle) Area() float64 {
    return this.length * this.width
}
func main() {
    var circle Circle
    circle.radius = 3
    fmt.Println(circle.Area())
    rec := Rectangle{5.0, 8}
    fmt.Println(rec.Area())
}

3.2. 方法与函数

方法与函数是类似的,区别在于方法是一类带有接受者(也就是实例)的特殊函数

所以在Go语言里面,方法可以看成是带有类型的函数

a Go method is a function that acts onvariable of a certain type, called the receiver. So a method is a specialkindof function ——《The Way to Go》

3.3. defer关键字

defer是延迟调用,执行方式是先进后出,常用于释放资源如关闭文件写入,关闭数据库连接等

与Python中的with语法较为相似

代码例子如下

Go
package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 5; i++ {
    	defer fmt.Println(i)
    }
}

// 执行输出如下
 go run main.go                          
4
3
2
1
0

3.4. Json

Go语言作为强类型语言,处理Json不如脚本语言方便

在处理Json上,Go采用了标签标识的方法来解决json字段与结构体字段对应的问题

如下面代码所示,其`json:"username"` 就是标签标识

除了标识对应关系外,还可以添加例如omitempty来标识序列化时忽略0值或Nil值

代码如下

Go
package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name   string `json:"username"`
    Age    int    `json:"age,string"`
    Gender int    `json:"gender"`
    Score  int    `json:"score,omitempty"`
}

func main() {
    var data string = `{ "username":"chancel", "age":"18", "gender":0, "Score":666 }`
    var s = &Student{}
    var err = json.Unmarshal([]byte(data), s)
    fmt.Println(err)
    fmt.Println(s)

    s.Score = 0

    var jsonData, _ = json.Marshal(s)
    fmt.Println(string(jsonData))

}

// 输出如下
 go run main.go
<nil>
&{chancel 18 0 666}
{"username":"chancel","age":"18","gender":0}

资料引用


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]