• 【Go语言从入门到实战】基础篇


    Go语言从入门到实战 — 基础篇

    First Go Program

    编译 & 运行

    image-20230426195714507

    基本程序结构

    image-20230426200702701

    应用程序入口

    image-20230426200721500

    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello World")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    退出返回值

    image-20230426200739987

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	fmt.Println("Hello World")
        os.Exit(-1)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20230426201336110

    获取命令行参数

    image-20230426200753974

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
    	fmt.Println(os.Args[0])
    	fmt.Println(os.Args[1])
    	fmt.Println("Hello World")
    	os.Exit(-1)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    argslyc

    Test

    编写测试程序

    image-20230426201803096

    package test
    
    import "testing"
    
    func TestFirstTry(t *testing.T) {
    	t.Log("My first try!")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    image-20230426202100002

    变量 & 常量

    变量如果定义后未使用,则编译不通过,这是Go语言的特性,极大的减少无效内存。

    实现Fibonacci数列

    package fib
    
    import (
        "fmt"
        "testing"
    )
    
    func TestFibList(t *testing.T) {
       	var a int = 1
        var b int = 1
        //var (
        //	a int = 1
        //	b     = 1
        //)
        //a := 1
        //b := 1
        fmt.Print(a)
        for i := 0; i < 5; i++ {
          	fmt.Print(" ", b)
          	tmp := a
          	a = b
            b = tmp + a
        }
        fmt.Println()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    image-20230426202240328

    变量赋值

    image-20230426202149323

    // 变量交换
    func TestExchange(t *testing.T) {
        a := 1
        b := 2
        // 不需要tmp中间值
        //tmp := a
        //a = b
        //b = tmp
        a, b = b, a
        t.Log(a, b)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20230426202647390

    常量定义

    image-20230426202722084

    package constant_test
    
    import "testing"
    
    const (
        Monday = 1 + iota
        Tuesday
        Wednesday
    )
    
    func TestConstantTry(t *testing.T) {
        t.Log(Monday, Tuesday, Wednesday)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    image-20230426202813313

    package constant_test
    
    import "testing"
    
    const (
    	Readable   = 1 << iota // 可读 最后一位为1
    	Writable               // 可写 倒数第二位为1
    	Executable             // 可执行 倒数第三位位3
    )
    
    func TestConstantTry1(t *testing.T) {
    	a := 7 // 0111
    	t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20230426202932394

    数据类型

    基本数据类型

    image-20230426203020954

    • byteuint8 别名,无符号8位整型
    • rune:代表 Unicode 编码值,跟字符串 string 相关,后面讲
    • comlpex:复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位
      • TODO 不知道怎么用

    类型转换

    image-20230426203607582

    package type_test
    
    import (
        "math"
        "testing"
    )
    
    type MyInt int64
    
    func TestImplicit(t *testing.T) {
        var a int = 1
        var b int64
      	// 显示类型转换(不支持隐式转换)
        b = int64(a)
      	//b=a // Cannot use 'a' (type int) as the type int64
        var c MyInt
        c = MyInt(b) // 别名类型也不可以隐式转换
        t.Log(a, b, c)
    
        t.Log(math.MaxInt) // 类型的预定义值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    指针

    不支持🙅🏻‍♀️指针运算。

    // 指针
    func TestPoint(t *testing.T) {
        a := 1
        aPtr := &a
        //aPtr += 1 // go语言不支持指针运算
        t.Log(a, aPtr)
        t.Logf("%T %T", a, aPtr)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    image-20230427095947395

    字符串

    string是值类型,其默认的初始化值为空字符串,而不是 nil

    // 字符串
    func TestString(t *testing.T) {
        var s string
        t.Log("*" + s + "*") // string默认是空字符串
        t.Log(len(s))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20230427100059787

    修改字符串
    func TestChangeString(t *testing.T) {
    	s1 := "big"
    	// 强制类型转换
    	byteS1 := []byte(s1)
    	byteS1[0] = 'p'
    	t.Log(string(byteS1))
    
    	s2 := "白萝卜"
    	runeS2 := []rune(s2)
    	runeS2[0] = '红'
    	t.Log(string(runeS2))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运算符

    算术运算符

    A := 10
    B := 20
    
    • 1
    • 2

    image-20230427100228162

    比较运算符

    A := 10
    B := 20
    
    • 1
    • 2

    image-20230427101007973

    == 比较数组

    image-20230427101419905

    // 数组的比较
    func TestCompareArray(t *testing.T) {
    	a := [...]int{1, 2, 3, 4}
    	b := [...]int{1, 3, 4, 5}
    	//c := [...]int{1, 4, 5, 6, 7}
    	d := [...]int{1, 2, 3, 4}
    	t.Log(a == b)
    	//t.Log(a == c) // 无法比较长度不同的数组
    	t.Log(a == d)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20230427101434930

    逻辑运算符

    image-20230427101542130

    位运算符

    A := 60 // 0011 1100
    B := 13 // 0000 1101
    
    • 1
    • 2

    image-20230427111130058

    按位清零

    Go的特性:&^

    image-20230427111250087

    package operator_test
    
    import "testing"
    
    const (
    	Readable   = 1 << iota // 可读 最后一位为1
    	Writable               // 可写 倒数第二位为1
    	Executable             // 可执行 倒数第三位位3
    )
    
    // 按位清零
    func TestBitClear(t *testing.T) {
    	a := 7
    	a &^= Readable // 清零
    	t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20230427111420383

    循环

    image-20230427131239226

    代码示例

    image-20230427131310592

    条件

    if

    image-20230427131606448

    image-20230427131740986

    func TestIfMultiSec(t *testing.T) {
    	if a := 1 == 1; a {
    		t.Log("1 == 1")
    	}
    	// 比较常用的if使用
    	/*if v, err := someFun(); err == nil {
    		t.Log(v)
    	} else {
    		t.Log("error!")
    	}*/
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    switch

    image-20230427132038273

    switch 中的 case 自带 break。

    // go的switch支持多个变量
    func TestSwitchMultiCase(t *testing.T) {
    	for i := 0; i < 5; i++ {
    		switch i {
    		case 0, 2:
    			t.Log("偶数")
    		case 1, 3:
    			t.Log("奇数")
    		default:
    			t.Log("not 0-3")
    		}
    	}
    }
    
    // go的switch还支持条件表达式
    func TestSwitchCaseCondition(t *testing.T) {
    	for i := 0; i < 5; i++ {
    		switch {
    		case i%2 == 0:
    			t.Log("偶数")
    		case i%1 == 0:
    			t.Log("奇数")
    		default:
    			t.Log("not 0-3")
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    数组

    数组的声明

    image-20230426201812637

    func TestArrayInit(t *testing.T) {
    	var arr [3]int
    	arr1 := [4]int{1, 2, 3, 4}
    	arr2 := [...]int{1, 3, 5, 7} // 如果不知道需要创建多大的空间 可用'...'
    	t.Log(arr[1], arr1[1], arr2[1])
    
    	arr3 := [...][3]int{{3, 4}, {1, 2, 3}} // 二维数组只有第一维可以用'...' 二维必须是确定的值
    	t.Log(arr3[0][0])
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    数组元素遍历

    image-20230426201848179

    func TestArrayTravel(t *testing.T) {
    	arr := [...]int{1, 3, 4, 5}
    	for i := 0; i < len(arr); i++ {
    		t.Log(arr[i])
    	}
    
        // 类似java增强for循环
    	for idx, e := range arr {
    		t.Log(idx, e)
    	}
        
        // 如果不想要idx这个值,则可以使用下划线'_'(空标识符)
        // 空标识符可用于任何语法需要变量名但程序逻辑不需要的时候,
        // 例如, 在循环里,丢弃不需要的循环索引, 保留元素值。
        for _, e := range arr {
    		t.Log(e)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    数组截取

    image-20230426201859153

    func TestArraySection(t *testing.T) {
    	arr := [...]int{1, 2, 3, 4, 5}
    	arrSec := arr[:3]
    	t.Log(arrSec)
    	arrSec2 := arr[3:]
    	t.Log(arrSec2)
        arrSec3 := arr[0:3] // [0, 3)
    	t.Log(arrSec3)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20230427132834106

    切片

    切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。

    切片是一个引用类型(结构体),它的内部结构包含地址长度容量。切片一般用于快速地操作一块数据集合。

    切片内部结构

    image-20230426201919359

    切片声明

    image-20230427140537065

    func TestSliceInit(t *testing.T) {
    	var s0 []int // 声明方式很像数组,但它不需要声明大小,因为它是可变长的
    	t.Log(len(s0), cap(s0))
    	s0 = append(s0, 1)
    	t.Log(len(s0), cap(s0))
    
    	s1 := []int{1, 2, 3, 4}
    	t.Log(len(s1), cap(s1))
    
    	s2 := make([]int, 3, 5) // 使用make()函数,声明切片的长度为3,容量为5
    	t.Log(len(s2), cap(s2))
    
    	t.Log(s2[0], s2[1], s2[2]) // 只初始化了前3个元素
    	//t.Log(s2[3], s2[4]) // runtime error: index out of range [3] with length 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image-20230427140246470

    切片如何实现可变长

    func TestSliceGrowing(t *testing.T) {
    	var s []int
    	for i := 0; i < 10; i++ {
    		s = append(s, i)
    		t.Log(len(s), cap(s))
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    空间不够自动扩容,每次扩容为原来的2倍。

    所以为什么append()函数要写成这样:s = append(s, i),重新赋值给s,因为结构体指向的连续存储空间每次扩容都会发生变化,创建一个新的连续存储空间,把原来的值拷贝过来,所以需要重新赋值。

    所以我们不需要关注切片创建的大小,使用起来很方便,但也不能无限制的利用切片的这种特性,如果数据过多那么拷贝的代价是非常大的。

    image-20230429200943870

    切片共享存储结构

    image-20230429202216796

    func TestSliceShareMemory(t *testing.T) {
    	year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
    	Q2 := year[3:6]
    	t.Log(Q2, len(Q2), cap(Q2))
    
    	summer := year[5:8]
    	t.Log(summer, len(summer), cap(summer))
    
    	summer[0] = "UnKnow"
    	t.Log(Q2)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20230427143054133

    数组 vs 切片

    image-20230427143400881

    • 数组不可以伸缩,但可以比较。
    • 切片可以伸缩,但不可以比较。

    Map集合

    map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

    Map声明

    image-20230427143735245

    func TestMapInit(t *testing.T) {
    	m1 := map[int]int{1: 1, 2: 4, 3: 9}
    	t.Log(m1[2])
    	t.Logf("len m1 = %d", len(m1))
    	m2 := map[int]int{}
    	m2[4] = 16
    	t.Logf("len m2 = %d", len(m2))
    	m3 := make(map[int]int, 10)
    	t.Logf("len m3 = %d", len(m3))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20230427144235345

    Map中value的默认值

    /*
      我们获取map的key有两种情况
      1.key不存在
      2.key存在,但value为空
    */
    func TestAccessNotExistingKey(t *testing.T) {
    	m := map[int]int{}
    	t.Log(m[1]) // 不存在的value值,为0
    	m[2] = 0    // 我们将key为2的value设置为0
    	t.Log(m[2]) // value为0
    	// 也就是说go中的map,无论value是否存在,他们都会被默认赋值为0
    	// 这就跟java不一样了,java会返回null,则会造成空指针异常,而go就不会出现这个问题
    	// 但我们无法判断他是默认值还是我们赋的值,所以go需要我们自己判断这个值是否存在
    	if v, ok := m[3]; ok {
    		t.Logf("key 3's value is %d", v)
    	} else {
    		t.Log("key 3 is not existing.")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    image-20230427145058403

    Map的遍历

    func TestTravelMap(t *testing.T) {
    	m1 := map[int]int{1: 1, 2: 4, 3: 9}
    	for k, v := range m1 {
    		t.Log(k, v)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20230427150243724

    Map与工厂模式

    image-20230427150357082

    func TestMapWithFunValue(t *testing.T) {
    	m := map[int]func(op int) int{}
    	m[1] = func(op int) int { return op }
    	m[2] = func(op int) int { return op * op }
    	m[3] = func(op int) int { return op * op * op }
    	t.Log(m[1](2), m[2](2), m[3](2))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    image-20230524200651879

    实现Set

    image-20230427161213316

    // 使用map构建set
    func TestMapForSet(t *testing.T) {
    	mySet := map[int]bool{} // key的类型自己设定,value的类型只能是bool类型
    	mySet[1] = true
    	// 所以我们判断元素是否存在则直接判断value是否为true
    	for i := 1; i < 3; i++ {
    		if mySet[i] {
    			t.Logf("%d is existing", i)
    		} else {
    			t.Logf("%d is not existing", i)
    		}
    	}
    	delete(mySet, 1) // 删除元素
    	t.Log(mySet[1] == true)
        t.Log(len(mySet)) // 元素个数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20230427164327962

    字符串

    string

    image-20230427164433493

    func TestString(t *testing.T) {
    	var s string
    	t.Log(s) // 初始化默认零值""
    	s = "hello"
    	t.Log(len(s))
    	//s[1] = '3' // string是不可变的byte slice [cannot assign to s[1]]
    	s = "\xE4\xB8\xA5" // 可以存储任何二进制数据
    	t.Log(s)
    	t.Log(len(s))
    	s = "中"
    	t.Log(len(s)) // 是byte数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20230427170214683

    Unicode & UTF8

    image-20230427185713325

    func TestString(t *testing.T) {
    	var s string
    	s = "中"
    	t.Log(len(s)) // 是byte数
    
    	c := []rune(s) // rune 可以取出字符串里的unicode
    	t.Log(len(c))
    	// t.Log("rune size:", unsafe.Sizeof(c[0]))
    	t.Logf("中 unicode %x", c[0])
    	t.Logf("中 UTF8 %x", s)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20230427190155522

    编码与存储

    image-20230427191808339

    格式化输出

    func TestStringToRune(t *testing.T) {
    	s := "中华人民共和国"
    	for _, c := range s {
    		t.Logf("%[1]c %[1]d", c) // [1]就是只和第1个参数匹配,以%c和%d格式化
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20230427194822355

    常用函数

    func TestStringFun(t *testing.T) {
    	s := "A,B,C"
    	parts := strings.Split(s, ",") // 分割
    	for _, part := range parts {
    		t.Log(part)
    	}
    	t.Log(strings.Join(parts, "-")) // 拼接
    
    	s = strconv.Itoa(10)     // 整数转字符串
    	t.Log(reflect.TypeOf(s)) // 利用反射查看元素类型
    	a, _ := strconv.Atoi(s)  // 字符串转整形 会额外返回一个错误值
    	t.Log(reflect.TypeOf(a))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    函数

    函数式编程

    image-20230428104545386

    // 多个返回值的函数
    func returnMultiValues() (int, int) {
    	return rand.Intn(10), rand.Intn(20)
    }
    
    // 计算函数操作的时长
    func timeSpend(inner func(op int) int) func(op int) int {
    	// 类似装饰者模式,对原来的函数进行了一层包装
    	return func(n int) int {
    		start := time.Now()
    		ret := inner(n)
    		fmt.Println("time spend:", time.Since(start).Seconds())
    		return ret
    	}
    }
    
    // 休眠1s的函数
    func slowFun(op int) int {
    	time.Sleep(time.Second * 1)
    	return op
    }
    
    func TestFunc(t *testing.T) {
    	a, _ := returnMultiValues()
    	t.Log(a)
    
    	tsSF := timeSpend(slowFun) // 调用计算时长方法 传入一个函数
    	t.Log(tsSF(10)) // 输出我们传入的值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    可变参数

    image-20230428105748557

    func Sum(ops ...int) int {
    	ret := 0
    	for _, op := range ops {
    		ret += op
    	}
    	return ret
    }
    
    func TestVarParam(t *testing.T) {
    	t.Log(Sum(1, 2, 3))
    	t.Log(Sum(1, 2, 3, 4, 5))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20230524200907347

    延迟执行函数defer

    类似于 java 的 try/finally

    image-20230428110134493

    func Clear() {
    	fmt.Println("Clear resources.")
    }
    
    func TestDefer(t *testing.T) {
        defer Clear() // 使用defer关键字调用Clear()函数 即使出现异常也可以保证释放资源
    	fmt.Println("Start")
    	panic("err") // Exception 抛异常
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20230524201000231

    笔记整理自极客时间视频教程:Go语言从入门到实战

  • 相关阅读:
    SmartX 边缘计算解决方案:简单稳定,支持各类应用负载
    制作一个简单HTML电影网页设计(HTML+CSS)
    python&selenium自动化测试实战项目
    硬件加速绘制基础知识
    计算机网络-计算机网络体系结构-概述,模型
    FastRCNN
    [JAVA毕业设计源代码]精品微信小程序英语四六级小助手小程序系统|前后分离VUE[包运行成功]
    第一个 Go 程序“hello,world“ 与 main 函数
    HCIP第十六天笔记
    基于Linux的Spark安装与环境配置
  • 原文地址:https://blog.csdn.net/weixin_53407527/article/details/130854516