【青训营】01:GO基本语法

210 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

0 前言

Go是一门编译型语言,Go语言的工具链将源代码及其依赖转换成计算机的机器指令(译注:静态编译)。Go语言提供的工具都通过一个单独的命令go调用,go命令有一系列子命令。

1 基本语法一览

1.1 包和文件

go语言的包类似于java的package,cpp的#include。目的都是用于模块化,封装,单独编译,代码重用。每个包对应一个独立的名字空间,比如引入fmt,就要通过fmt.Sprinltf来访问里面的函数。包的命名通常与包的所在目录相同。

1.2 声明

Go语言主要有四种类型的声明语句:varconsttypefunc,分别对应变量常量类型函数实体对象的声明

1.3 控制语句

同样,Go支持ForIfswitch三个控制语句

值得注意的是

  1. For支持range操作,也就是如果定义了一个数组nums,可以使用for i, num := range nums的方式遍历
  2. If不包含(),并且一定要执行语句加{}
  3. switch的每个case不想cpp,java一样要加break才可以防止执行下一个case。想要执行后面的case需要添加fallthrough

1.4 数据类型

数组

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。

 // 一维
 var a [5]int
 // 二维
 var twoD [2][3]int
 // 如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。
 q := [...]int{1, 2, 3}
 // 修改不同索引下的数值,数组长度为100
 r := [...]int{101, 99: -1}

由于数组的僵化了长度信息,通常只会用在生成摘要,加密等特定大小的信息,一般使用slice代替数组

Slice

Slice(切片)表示可变长的序列,每个元素都有相同的类型。一个slice由三个部分构成:指针长度容量

创建方式:

 // 通过数组来定义切片
 var array [5]int
 slice1 := array[:] // 拷贝全部
 // 使用make函数
 slice1 := make([]int, 5)
 // 直接初始化
 slice1 :=[]int{1,2,3} // 和数组最大的不同在于[]没有数字

追加函数Append与扩容:

 s := make([]string, 3)
 s[0] = "a"
 s[1] = "b"
 s[2] = "c"
 s = append(s, "d")
 fmt.Println(s) // [a b c d]

slice的扩容采用2倍扩容的方式,当len=cap时,触发扩容

 func main() {
     var x, y []int
     for i := 0; i < 10; i++ {
         y = append(x, i)
         fmt.Printf("%d cap=%d\t len=%d\t %v\n", i, cap(y), len(y), y)
         x = y
     }
 }
 // 输出
 /*
 0 cap=1  len=1   [0]
 1 cap=2  len=2   [0 1]
 2 cap=4  len=3   [0 1 2]
 3 cap=4  len=4   [0 1 2 3]
 4 cap=8  len=5   [0 1 2 3 4]
 5 cap=8  len=6   [0 1 2 3 4 5]
 6 cap=8  len=7   [0 1 2 3 4 5 6]
 7 cap=8  len=8   [0 1 2 3 4 5 6 7]
 8 cap=16     len=9   [0 1 2 3 4 5 6 7 8]
 9 cap=16     len=10  [0 1 2 3 4 5 6 7 8 9]
 */

Map

无序的key/value对的集合。写为map[K]V,K必须支持==运算,如前面的slice就不能作为key,因为其不支持==计算。

创建方式:

 ages := make(map[string]int)
 ages["w1nd"] = 2
 ages["alice"] = 3

删除方式:

 delete(ages, "alice")   // 如果没有该key也没事

注意事项:

  1. 不允许对map元素进行取址操作,因为会动态增长,可能导致每次地址都不一样
  2. map迭代顺序不确定,也就是使用for...range遍历的顺序是随机的
  3. map自身也不能进行==比较

tips:虽然slice不能作为key,但可以通过一个辅助函数来进行转换

 var m = make(map[string]int)
 func k(list []string) string { return fmt.Sprintf("%q", list) }
 func Add(list []string)       { m[k(list)]++ }
 func Count(list []string) int { return m[k(list)] }

结构体

由零个或多个任意类型的值聚合成的实体。

创建方式:

 type user struct {
     name     string
     password string
 }
 // 访问直接用.
 var d user
 d.name = "wang"
 d.password = "1024"
 // 结构体指针
 var d1 *user
 d1 = &d

注意事项:

  1. 结构体可以进行==比较
  2. 允许结构体嵌套

结构体方法

此方法一般比其他普通方法多一个接收器。如下

 // 格式
 func (recv receiver_type) methodName(parameter_list) (return_value_list) { 
     ... 
 }
 // 栗子
 func (u *user) resetPassword(password string) { // 注意:在接收器是指针时,方法可以改变接收器的值
     u.password = password
 }

1.5 String

字符串,比较简单,列出几个常用API:

 fmt.Println(strings.Contains(a, "ll"))                // true,判断是否包含该字符串
 fmt.Println(strings.Count(a, "l"))                    // 2,返回该字符的数量
 fmt.Println(strings.HasPrefix(a, "he"))               // true,是否是该前缀
 fmt.Println(strings.HasSuffix(a, "llo"))              // true,是否是该后缀
 fmt.Println(strings.Index(a, "ll"))                   // 2,下标
 fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo,拼接
 fmt.Println(strings.Repeat(a, 2))                     // hellohello,重复
 fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo,替换
 fmt.Println(strings.Split("a-b-c", "-"))              // [a b c],拆分
 fmt.Println(strings.ToLower(a))                       // hello,小写
 fmt.Println(strings.ToUpper(a))                       // HELLO,大写

2 常用库

2.1 fmt

输入输出标准库。

有以下API:

 Print:   输出到控制台,不接受任何格式化操作
 Println: 输出到控制台并换行
 Printf : 只可以打印出格式化的字符串。只可以直接输出字符串类型的变量(不可以输出别的类型)
 Sprintf:格式化并返回一个字符串而不带任何输出
 Fprintf:来格式化并输出到 io.Writers 而不是 os.Stdout

占位符

 %v     值的默认格式。
 %+v   类似%v,但输出结构体时会添加字段名
 %#v  相应值的Go语法表示 
 %T    相应值的类型的Go语法表示 
 %%    百分号,字面上的%,非占位符含义

2.2 json

json解析库。

 func Marshal(v interface{}) ([]byte, error) // 序列化,一个是序列化对象
 func Unmarshal(data []byte, v interface{})  // 反序列化,一个是需要被序列化的对象,另一个是表示这个对象的类型。

2.3 time

go的时间库,有如下API:

 now := time.Now()
 // *时间类型*
 fmt.Printf("当前时间是:%s\n", now.String())
 fmt.Printf("%d年", now.Year())
 fmt.Printf("%d月", now.Month())
 ...
 // *时间戳*
 timestamp1 := now.Unix()     //时间戳
 timestamp2 := now.UnixNano() //纳秒时间戳
 // *时间间隔*
 now := time.Now() //获取当前时间
 // 时间间隔4000秒
 endTime := now.Add(time.Second * 4000)
 // 计算时间间隔
 Duration := time.Unix(0, endTime.Sub(now).Nanoseconds())
 // *定时器*
 tick := time.Tick(time.Second) // 倒计时1s

2.4 strconv

实现了基本数据类型与其字符串表示的转换。有如下API

 // 将字符串类型的整数转换为int类型
 func Atoi(s string) (i int, err error)  
 // 将int类型数据转换为对应的字符串表示
 func Itoa(i int) string
 // 返回字符串表示的整数值,接受正负号。
 func ParseInt(s string, base int, bitSize int) (i int64, err error)
 // 解析一个表示浮点数的字符串并返回其值。
 func ParseFloat(s string, bitSize int) (f float64, err error)

。。。

2.5 env

通过os包来获取环境变量

 // go run example/20-env/main.go a b c d
 fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
 fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
 fmt.Println(os.Setenv("AA", "BB"))
 ​
 buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
 if err != nil {
     panic(err)
 }
 fmt.Println(string(buf)) // 127.0.0.1       localhost

3 总结

本次课程主要学习GO的基本语法以及常用库,本文只是简单总结了下,并且课程也有讲了三个实战小项目,由于涉及内容过多,这里就不阐述了。