Go语言基础 - 基础语法 | 青训营笔记

118 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第1天

主要内容

Go语言介绍

Go语言的优势:

  1. 高性能、高并发
  2. 语法简单
  3. 标准库丰富
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

Go语言开发环境配置

我推荐使用GoLand进行开发,比较容易上手,对于go的sdk版本也很好下载

Go语言基础语法

变量及常量

变量声明方式

var a = "initial" // 自动推断类型
var a string = "initial" // 显式说明类型
a := "initial" // 自动推断类型

Go语言中没有隐式类型转换,我们在使用时都需要用显式类型转换,例如:

var e float64
f := float32(e)

常量声明方式

const s string = "constant"
const h = 5000000
const i = 3e20 / h

条件语句

if-else

if 7 % 2 == 0 {
    fmt.Printf("7 is even")
} else {
    fmt.Printf("7 is odd")
}

其实和C、CPP相比就是少了个()

switch

switch a {
    case 1:
        fmt.Printf("a = 1")
    case 2:
        fmt.Printf("a = 2")
    case 3: 
        fallthrough
    default:
        fmt.Printf("other")
}

这里需要注意的是,Go语言中不需要加break,如果我们需要执行两个分支的话,需要加关键字fallthrough

select

这个课程中没有,可以大概讲一下

c := make(chan int, 1)
c <- 1
select {
    case <-c:
        fmt.Printf("random 01")
    case <-c:
        fmt.Printf("random 02")
}
  • select的特性之一是会随机选择满足条件的几个分支之一
  • 这段代码的意思就是先往chan里写入1,然后select多路复用,发现<-c都满足条件,所以会随机选择一个分支执行

循环语句

Go里面没有while循环,只有for循环

// 死循环
for {
    fmt.Printf("1")
}
// 实现while
i := 0
for i < 4 {
    fmt.Printf(i)
    i++
}
// 正常使用
for i := 0; i < 4; i++ {
   fmt.Printf(i)
}

数组

声明

var a [3]int

以上声明了一个大小为3的int数组

访问

有两种方式:

  1. for循环
    for i := 0; i < 3; i++ {
        fmt.Printf(a[i])
    }
  1. for-range
    for index, value := range a {
        fmt.Printf(index, value)
    }

注意这里的index是指下标,value即对应数组下标的值

切片

声明

a := make([]int, 3)

以上声明了一个len为3的int切片

good := string[]{"g", "o", "o", "d"}

以上声明了一个len为4的切片,并且内容也声明了

使用

append函数的使用,我们可以将一个元素push到切片的末尾

a := []int{1, 2, 3}
a = append(a, 4)
// a --> [1, 2, 3, 4]

注意扩容无须人为干涉,Go会自己实现扩容

map

和cpp里面的map使用类似

声明

a := make(map[string]int, 3)

以上声明了一个大小为3,key为键,int为值的map

访问

for key, value := range a {
    fmt.Printf(key, value)
}

然后如果需要访问某一个key的value的话,有以下两种方式

value := a[key]
value, ok := a[key]

ok代表map中是否有这个key

函数

在Go语言中,函数是一等公民,这意味着可以将它看作变量,并且它可以作为参数传递、返回及赋值

// 函数作为返回值
func makeGreeter() func() string {
    return func() string {
        return "hello jonson"
    }
}

// 函数作为参数
func visit(numbers []int, callback func(int)) {
    for _, n := range numbers {
        callback(n)
    }
}

Go中函数还具有多返回值的特点,多返回值最常用于返回error错误信息,从而被调用者捕获。

func dlv(a int, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("b<0")
    }
    return a/b, nil
}
func main() {
    if _, err := dlv(4, 2); err != nil {
        fmt.Println("err:",err)
    }
}

指针

声明

var a *int

以上声明了一个指向int的指针,其他类型类似

结构体

声明

type User struct {
    UserId string
    UserName string
    Password string
}

以上声明了一个User的结构体,其中有三个string字段,分别是UserId,UserName,Password

赋值

var d user
d.UserName="Yuuki"
a := {UserName:"Yuuki"}
b := {"123", "Yuuki", "12983"} // 这里是按顺序赋值的

结构体方法

类似于类函数

func (u user) hello() {
    fmt.Println("hello world")
}

上面表示的是绑定了user的方法,调用时需要用到user实例

错误处理

Go里面有很多函数都会有多个返回值,其中有一个就是error类型返回值,这样的话,我们能自己处理err,比如说在上述map中获取key的value时返回的ok,虽然他不是err,但是也算是一个错误吧(我认为)。实际上,例如http的listen之类的函数都会有error返回值

字符串函数

参考doc.golang.ltd/

Json的处理

两个重要的函数json.Marshal json.Unmarshal,具体使用参考课程

时间的处理

参考doc.golang.ltd/

数字解析

f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234

n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111

n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096

n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123

n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax

ParseFloat、ParseInt第二个参数为解析的进制

进程信息

// 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    

os.Args代表命令行参数,os.Getenv获取环境变量,os.Setnv设置环境值的值,exec.Command开一个子进程执行任务

个人总结

  1. 通过这次课程我快速地学会了golang的基础语法以及回忆了一些之前学过的golang底层的实现,但是这次课程也有很多比较重要的东西没有讲,比如说chan,select等等,这些都是需要我在课后去学习的。
  2. 三个实例对我前面的基础语法也算是有个小总结,并且其中用到了许多共享库,这些都是之前学习时所欠缺的,尤其是第三个实例->代理,一开始如果直接看代码的话可能会有些找不到重点,所以就需要回忆出课程中所放的图片,有三个阶段(上课是说的四个,我认为是三个?),每一步都有它存在的必要性,然后根据那个图,慢慢的实现就可以了