Go 语言入门指南 | 青训营

121 阅读9分钟

前言 

    Go(又称 Golang)

    Go语言是编译型、静态类型的类C的语言,并带有GC(垃圾收集器,garbage collection)。

Go是一种非常严格的语言,它几乎总是要求我们"以标准答案去答题",在其它语言可以容忍的不规范编码方式在Go语言中几乎都会抛异常。例如导入了包却没有使用这个包,Go不会去编译它并报错。再例如,定义了一个变量但从来没用过,也会报错。

关键字

第一个程序

package mainimport "fmt"func main() {    fmt.Println("hello world!")}
  1. package main 代表这个文件属于main包的一部分,main包也就是程序的入口包。
  2. import 导入了标准库里面的FMT包。这个包主要是用来往屏幕输入输出字符串、格式化字符串。
  3. main 函数的话里面调用了fmt.Println输出helloword

变量

    常见的变量类型包括字符串、整数型、浮点型、布尔型等。 

    go 语言的字符串是内置类型,可以直接通过加号拼接,也能够直接用等于号去比较两个字符串。 

    在 go 语言里面,大部分运算符的使用和优先级都和C或者C++类似,这里就不再概述。 

    在 go 语言里面变量的声明有两种方式,一种是通过 var name String = " " 。声明变量的时侯,一般会自动去推导变量的类型。如果有需要,你也可以显示写出变量类型。另一种声明变量的方式是:    使用变量冒号 := 等于值。 常量的话就是把 var 改成 const,值在一提的是 go 语言里面的常量,它没有确定的类型,会根据使用的上下文来自动确定类型。

go语言的流程控制

go语言中的条件

       条件语句是用来判断给定的条件是否满足(表达式值是否为true或者false),并根据判断的结果(真或假)决定执行的语句,go语言中的条件语句也是这样的。 

      go语言中的条件语句包含如下几种情况 if 语句:if 语句 由一个布尔表达式后紧跟一个或多个语句组成。

       if...else 语句: if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 if 嵌套语句: 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。

  1. go 语言里面的 if else 写法和 C 或者 C++ 类似。不同点是 if 后面没有括号。
  2. 第二个不同点是 Golang 里面的 if,它必须后面接大括号。

    switch 语句: switch 语句用于基于不同条件执行不同动作。 

    go 语言里面的 switch 分支结构。看起来也 C 或者 C++ 比较类似。同样的在 switch 后面的那个变量名,必须要括号。 这里有个很大的一点不同的是,在 C++ 里面,switch case 如果不加 break 的话会继续往下跑完所有的 case,在 go 语言里面的话是不需要加 break 的(fallthrough  关键字,判定为true时加上这个就会执行后面的,默认不会执行后面)。 相比 C 或者 C++,go 语言里面的 switch 功能更强大。可以使用任意的变量类型。 甚至可以用来取代任意的 if else 语句。你可以在 switch 后面不加任何变量,然后在 case 里面写条件分支,这样代码逻辑更清晰。

      select 语句: select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 

go语言中的循环语句

      go语言中的循环只有for循环,去除了while、do while循环,使用起来更加简洁。 for循环。 for range循环。 

      go语言中的流程控制关键字 break continue goto

package main
 
import (
    "fmt"
)
 
func main() {
    fmt.Println("aaa")
    fmt.Println("bbb")
     
    //var a = 100 
    a := 100 // 声明并定义
 
    if a > 20 {
        fmt.Println("a")
    } else {
        fmt.Println("b")
    }

数组

    数组就是一个具有编号且长度固定的元素序列。比如这里的话是一个可以存放 5 个 int 元素的数组 A : A:= [5]int{1, 2, 3, 4, 5}。 对于一个数组,可以很方便地取特定索引的值或者往特定索引取存储值,然后也能够直接去打印一个数组。不过,在真实业务代码里面,我们很少直接使用数组,因为它长度是固定的,我们用的要多的是切片。 

slice 切片

    切片不同于数组可以任意更改长度,然后也有更多丰富的操作。

    比如说我们可以用 make 来创建一个切片,可以像数组一样去取值,使用 append 来追加元素。注意 append 的用法的话,你必须把 append 的结果赋值为原数组。 因为切片的原理实际上是它存储了一个长度和一个容量,加一个指向一个数组的指针,在你执行 append 操作的时候,如果容量不够的话,会扩容并且返回新的切片,切片初始化的时候也可以指定长度。

    拥有像 python 一样的切片操作,比如这个代表取出第二个到第五个位置的元素,不包括第五个元素。不过不同于 python,这里不支持负数索引。

str := make([]string, 3)
str[0] = "a"
str[1] = "b"
str[2] = "c"
fmt.Println("get:", str[2])   // cfmt.Println("len:", len(str)) // 3
str = append(str, "d")
str = append(str, "e", "f")
fmt.Println(str) // [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]

map

    map 在其他编程语言里面,它可能可以叫做哈希或者字典。

    map 是实际使用过程中最频繁用到的数据结构。 

    用 make来创建一个空 map,这里会需要两个类型,第一个是 key 的类型,这里是 sting,另一个是 value 的类里,这里是 int。我们可以从里面去存储或者取出键值对。可以用 delete 从里面删除键值对。

    golang 的 map 是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。

m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)           // map[one:1 two:2]
fmt.Println(len(m))      // 2
fmt.Println(m["one"])    // 1
fmt.Println(m["unknow"]) // 0

r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false

delete(m, "one")

m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)

range

    对于一个 slice 或者一个 map 的话,我们可以用 range来快速遍历,这样代码能够更加简洁。range 遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值。如果我们不需要索引的话,我们可以使用下划线来忽略。

nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
   sum += num
   if num == 2 {
      fmt.Println("index:", i, "num:", num) // index: 0 num: 2
   }
}
fmt.Println(sum) // 9

函数

  1. Golang 和其他很多语言不一样的是,变量类型是后置的。

  2. Golang 里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。

    func add(a int, b int) int { return a + b }

    func exists(m map[string]string, k string) (v string, ok bool) { v, ok = m[k] return v, ok }

指针

Go 里面也支持指针。当然,相比 C 和 C++ 里面的指针,支持的操作很有限。指针的一个主要用途就是对于传入参数进行修改。

结构体与方法

    Go 语言结构体是实现自定义类型的一种重要数据类型。

    结构体是复合类型(composite types),它由一系列属性组成,每个属性都有自己的类型和值的,结构体通过属性把数据聚集在一起。

方法(Method)可以访问这些数据,就好像它们是这个独立实体的一部分

结构体是值类型,因此可以通过 new 函数来创建

    结构体是由一系列称为字段(fields)的命名元素组成,每个元素都有一个名称和一个类型。 字段名称可以显式指定或隐式指定,没有显式字段名称的字段称为匿名(内嵌)字段。在结构体中,非空字段名称必须是唯一的

type outerS struct {
    b int
    c float32
}
func main() {
    outer := new(outerS)
    outer.b = 6
    outer.c = 7.5
}

方法的定义

    在 Go 语言中,结构体就像是类的一种简化形式,那么面向对象程序员可能会问:类的方法在哪里呢?在 Go 语言中有一个概念,它和方法有着同样的名字,并且大体上意思相近。

    Go 语言中方法和函数在形式上很像,它是作用在接收器(receiver)上的一个函数,接收器是某种类型的变量。因此方法是一种特殊类型的函数,方法只是比函数多了一个接收器(receiver),当然在接口中定义的函数我们也称为方法(因为最终还是要通过绑定到类型来实现)。

type A struct {
    Face int
}

func (a A) f() {
    fmt.Println("hi ", a.Face)
}

    上面代码中,我们定义了结构体 A ,注意 f() 就是 A

的方法,(a A) 表示接收器。a是 A 的实例,f() 是它的方法名,方法调用遵循传统的 object.name 即选择器符号:a.f()

错误与异常处理机制

    Go语言中的error类型实际上是抽象了Error()方法的error接口

type error interface {
    Error() string
}

    Go语言使用该接口进行标准的错误处理。

对于大多数函数,如果要返回错误,大致上都可以定义为如下模式,将error作为多种返回
值中的最后一个,但这并非是强制要求:

func Foo(param int)(n int, err error) {
    // ...
}

个人总结

这是我第一次写Go的笔记,深知还有很大的不足之处,比如有些地方细节没有写完整。相信能在学习Go语言的道路上不断完善自己的知识。

Go语言的语法简单易懂(因为有其他语言基础),上手较为容易。基本了解了go的简单语法,随着更深入,希望能跟Go语言做一个“好朋友”

在学习过程中,我掌握了变量和常量的声明与使用,了解了各种控制流程语句,如if、for和switch等。我还学习了如何定义和使用函数,并且掌握了数据类型包括数组、切片、映射等。

另外,Go语言的面向对象编程方式不同于传统的类和继承,而是通过结构体和方法实现。这种方式与我写过的C++语法不太一样。

总之,学习Go语言是一次愉快而充实的过程。