简介

  1. GO诞生于2009年,官网
  2. 静态编译型语言
  3. 强类型语言

创始人

  1. Ken Thompson
  2. Rob Pike
  3. Robert Griesemer
  1. 肯 汤普森
  2. 罗伯 派克
  3. 罗伯特 格瑞史莫

目标

  1. 简单
  2. 高效
  3. 生产力

借鉴多门编程语言,新时代的C

设计哲学

  1. 简单
  2. 显式
  3. 组合
  4. 并发
  5. 面向工程

类型

普通类型

$GOPATH/src/builtin/builtin.go

type-study/type-study_test.go

https://www.educative.io/answers/what-is-type-uintptr-in-golang

int

  1. int
  2. int8
  3. int16
  4. int32
  5. int64

uint

  1. uint
  2. uint8
  3. uint16
  4. uint32
  5. uint64
  6. uintptr

不带数字的为平台相关的类型

float

  1. float32
  2. float64

bool

  1. true
  2. false
1
2
var a bool = true
var b bool = false

string

1
2
3
4
5
6
7
8
9
var s string = "hello"
fmt.Println(s)
fmt.Println(len(s))

s += " world"
fmt.Println(s)

fmt.Println(s[0])
fmt.Printf("s is %c", s[0])

type-string/string_test.go

内部指向包含Data与len的结构

类型转换

JAVA

1
2
int a = 1;
long b = a;

GO显示转换

1
2
3
4
5
var a int32 = 1
var b int64
//b = a // cannot use a (type int32) as type int64 in assignment
b = int64(a)
fmt.Println(b)

自定义类型

1
2
3
4
5
6
7
8
9
type MyInt int64
type IntConv func(op int) int

var a MyInt = 1
fmt.Println(a)

// var b int64 = a
var c int64 = int64(a)
fmt.Println(c)

类型别名

  1. go 1.9加入
  2. 在大规模的重构项目代码的时候
  3. 支持为其它包定义别名
  4. 少写变量
1
2
3
4
5
6
7
8
type S = string
type MyTime = time.Time

var s S = "hello"
fmt.Printf("%+v\n", s)

var t MyTime = time.Now()
fmt.Printf("%+v\n", t)

类型别名与类型定义的区别

  • 类型别名和原类型是相同的
  • 类型定义和原类型是不同的两个类型
1
2
3
4
5
6
7
type S = string
type MyString string

var s1 S
var s2 MyString
fmt.Printf("%T\n", s1)
fmt.Printf("%T\n", s2)

集合

数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var arr [5]int
fmt.Println(arr)
arr[0] = 8
fmt.Println(arr)

arrInit := [5]int{1, 2, 3, 4, 5}
fmt.Println(arrInit)

arrExpand := [...]int{2, 3, 4, 5, 6, 7}
fmt.Println(arrExpand)

arrWithIndexInit := [5]int{
    2: 101,
}
fmt.Println(arrWithIndexInit)

for i, v := range arrWithIndexInit {
    fmt.Println(i, v)
}

数组是不可变的

array/array_test.go

切片

1
2
3
4
5
6
7
8
9
var s0 []int
fmt.Println(s0, len(s0), cap(s0))

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]
fmt.Println(slice)

var sliceMake = make([]int, 2, 4)
fmt.Println(sliceMake)

编程常用结构,类比JAVA的ArrayList 关注len与capacity

切片转数组指针

  • Go 1.17引入
1
2
3
4
5
slice := []int32{1, 2, 3}
// from go 1.17, convert array length must be less then or equals slice length
arr := (*[3]int32)(slice)
fmt.Printf("%+v\n", arr)
fmt.Printf("%T\n", arr)

转换的数据指针长度只可小于等于切片长度

Map

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
me := map[string]string{}
fmt.Println(me)

m := map[string]string{
    "name":    "tomyli",
    "address": "beijing",
}
fmt.Println(m)

m3 := make(map[int]int, 10)
fmt.Println(len(m3))

//set
m["address"] = "shanghai"
//exist
v, ok := m["address"] // ok is true
v, ok = m["age"] // ok is false
fmt.Println(v, ok)
//iterate comma, ok
for k, v := range m {
    fmt.Println(k, v)
}
// delete
delete(m, "address")

指针

1
2
3
4
5
6
7
a := 1
aPtr := &a
//aPtr = aPtr + 1
fmt.Println(a)
fmt.Println(aPtr)
fmt.Printf("a Ptr type is %T", aPtr)
fmt.Println(*aPtr)

GO不允许直接对指针进行计算

结构体

struct/struct_test.go

1
2
3
4
5
type User struct {
    Name    string
    Age     int
    Address string
}

嵌入结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Address struct {
    Name string
    Code string
}

type User struct {
    Name    string
    Age     int
    Address Address
}

使用结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type User struct {
    Name    string
    Age     int
    Address string
}

var user User
fmt.Println(user)

user1 := User{
    Name: "John",
    Age:  1,
}
fmt.Println(user1)

空结构体

1
type User struct{}

常用作事件消息通信

接口类型

空接口

1
2
3
4
5
6
7
8
var i interface{}
i = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)

var j any // add from 1.18
fmt.Println(j)

自定义接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

type MyInterface interface {
    M1()
}

type Person struct {
    Name string
}

func (p Person) M1() {
    fmt.Println("Hello M1")
}

func main() {
    p := Person{}
    p.M1()
}

零值可用

可以直接在声明变量上进行调用

1
2
3
4
5
6
7
8
9
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)

var mu sync.Mutex
mu.Lock()
mu.Unlock()
  1. 不用担心变量的初始化状态

https://go.dev/tour/basics/12

变量

作用域

  1. 全局变量
  2. 局部变量

声明

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

var num = 10

func sum() {
    var name = "tomyli"
    fmt.Println(name)

    var a, b = 100, "200"
    fmt.Println(a, b)
}

func main() {
    // fmt.Println(name)
    fmt.Println(num)
    sum()
}

variable-use/var_test.go

常量

1
2
3
4
5
6
7
8
9
const lang = "english"
const (
    JAVA = "java"
    GO = "go"
)

const ua = 10
var b int64 = 100
fmt.Println(ua + b)

无类型常量,隐式转型

枚举

iota

  1. 值从0开始
  2. 递增1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const (
    c0 = iota  // c0 == 0
    c1 = iota  // c1 == 1
    c2 = iota  // c2 == 2
)

const (
    a = 1 << iota  // a == 1  (iota == 0)
    b = 1 << iota  // b == 2  (iota == 1)
    c = 3          // c == 3  (iota == 2, unused)
    d = 1 << iota  // d == 8  (iota == 3)
)

结构化

if

1
2
3
4
5
6
7
b := 2
if c := 10; c > b {
    fmt.Println("c > b")
} else if d := 20; d > b {
    fmt.Println("d > b")
    // fmt.Println(c)
}

支持语句中定义变量,作用域为if语句代码块 演示作用域

for

1
2
3
4
5
6
7
8
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

arr := []int{1, 2, 3, 4, 5}
for i, v := range arr {
    fmt.Printf("index is %d, value is %d", i, v)
}

go中没有while循环

switch-case

  1. switch支持多种比较对象,只要类型支持比较操作
  2. 支持声明临时变量
  3. 支持表达式列表
  4. 取消默认执行下一句,想继续执行要使用 fallthrough
1
2
3
4
5
6
7
8
9
a := 10
switch a {
case 1, 3, 5:
    t.Log("a is odds")
case 10:
    t.Log("a is 10")
default:
    t.Log("a is not 1 or 10")
}

type switch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var x interface{}
x = "hello"
switch x.(type) {
case string:
    t.Log("x is string")
case int:
    t.Log("x is int")
default:
    t.Log("x is not string or int")
}

x必须为接口类型

函数

一等公民

  1. 声明为变量
  2. 做为参数
  3. 做为返回值

main()

  1. 程序的入口
  2. 无参数
  3. 无返回值
  4. main包包名要为main且只可有一个main函数

init()

  1. 先于main函数执行
  2. 多个按代码顺序执行

运行顺序: 常量->变量->init()

init()用途

  1. 用于修改包级变量
  2. 包复杂变量的初始化
  3. 注册模式

trpc-go的config

可变参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

func sum(nums ...int) int {
    s := 0
    for _, n := range nums {
        s += n
    }
    return s
}

func main() {
    result := sum(1, 2, 3, 4, 5)
    fmt.Println(result)
}

底层实现为数组

多返回值

1
2
3
4
5
package main

func returnMultiValues() (int, int) {
    return rand.Int(), rand.Int()
}

内建函数

append cap clear close complex copy delete imag len make max min new panic print println real recover

$GOPATH/src/builtin/builtin.go

面向对象编程

方法

  1. 必须属于某一个类型
  2. T与*T的方法可互调
  3. go不支持方法重载
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

type Person struct {
    Name string
}

func (p Person) Print() string {
    return "the person name is " + p.Name
}

func (p *Person) PrintName() string {
    return "the person name is " + p.Name
}

func main() {

    person := Person{}
    person.Name = "tomyli"
    fmt.Println(person.Print())
    fmt.Println(person.PrintName())
}

方法表达式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

type Person struct {
    Name string
}

func (p Person) Print() string {
    return "the person name is " + p.Name
}

func (p *Person) Names() {
    p.Name += "123"
}

func main() {

    person := Person{}
    person.Name = "tomyli"
    fmt.Println(Person.Print(person))

    aPerson := person1.Names
    aPerson()
}

一个以方法的reciver参数作为第一个参数的普通函数

继承

  1. 不支持继承,支持复合
  2. 匿名类型嵌入
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
type S struct {
    io.Reader
    str string
}

func main() {
    r := strings.NewReader("hello, tomy")
    s := S{
        Reader: r,
        str: "world",
    }

    var sl = make([]byte, len("hello, tomy"))

    s.Reader.Read(sl)
    s.Read(sl) //here like extends
}

用继承来做什么,go如何实现继承

接口

  1. Duck Type式
  2. 可定义非导出方法

定义与实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type MyInterface interface {
    M1()
}

type T int

func (T) M1() {
    fmt.Println("T's M1")
}

func main() {
    var t T
    t.M1()
}

var mi MyInterface = t t, ok := mi.(T) fmt.Println(t, ok)

判断接口类型

1
2
3
4
5
6
var a int64 = 10
var i interface{} = a
v1, ok := i.(int64)
fmt.Printf("v1 type is %T", v1)
fmt.Println()
fmt.Printf("v1 type is int64? %t", ok)

类型为接口类型时,ok为true, v1为真正的类型

空接口

1
2
3
4
5
6
7
8
var i interface{}
// go 1.18
// var i any
fmt.Println(i)
i = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)

错误与异常

  1. 错误统一用error代表
  2. 异常使用panic()
1
2
3
4
5
6
7
8
err := errors.New("this is an error")
err1 := fmt.Errorf("this is an error %w", err)
err2 := fmt.Errorf("this is an error %w", err1)
fmt.Println(err)
fmt.Println(err1)
fmt.Println(err2)
fmt.Println(errors.Is(err1, err))
fmt.Println(errors.Is(err2, err))

panic

  1. 在go运行时触发
  2. 通过 panic() 触发
  3. 导致程序崩溃
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

func foo() {
    fmt.Println("foo start")
    panic("no no no")
    fmt.Println("foo end")
}

func main() {
    fmt.Println("main start")
    foo()
    fmt.Println("main end")
}

recover

异常恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

func foo() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("got panic")
        }
    }()
    fmt.Println("foo start")
    panic("no no no")
    fmt.Println("foo end")
}

func main() {
    fmt.Println("main start")
    foo()
    fmt.Println("main end")
}

异常恢复的代码要放在最上面可运行的地方

工程化

包是go语言的基本组成单元,通常使用小写单词命名

整个工程只允许有一个main包

首字母大写的标识符才是包外可见的

输出的包分为可执行包与库类型包

go.mod

定义模块名,go版本,依赖包信息

go.sum与go.mod中对应的md5信息,保证开发版本一致

工程根目录为go.mod文件所在的目录

资源释放

defer

  1. 用于资源清理
  2. 定义的方法执行时先进后出
  3. defer代码执行时,参数就会被绑定
  4. defer可以读取命名返回值
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

func ReleaseCon() {
    fmt.Printf("%+v\n", "release connection")
}

func main() {
    defer func() {
        fmt.Printf("%+v\n", "hello")
    }()

    defer ReleaseCon()

    fmt.Printf("%+v\n", "start")
}

并发

位于 sync 包下

  1. Mutex 互斥锁
  2. RWMutex 互斥读写锁
  3. WaitGroup 等待多个任务完成
  4. Cond 条件变量
  5. Map 并发安全的Map
  6. Once 只执行一次

Channel

用于goroutine之间通信

1
2
3
4
5
6
7
var ch chan int
ch1 := make(chan int)
bufch := make(chan int, 10)

fmt.Printf("%+v, type is %T \n", ch, ch)
fmt.Printf("%+v, type is %T \n", ch1, ch1)
fmt.Printf("%+v, type is %T \n", bufch, bufch)

后续对并发进行单独分享

泛型

GO1.18 新特性

 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
package main

type ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 |
        ~string
}

type mystring string

func maxGeneric[T ordered](sl []T) T {
    if len(sl) == 0 {
        panic("empty input")
    }
    max := sl[0]
    for _, v := range sl[1:] {
        if v > max {
            max = v
        }
    }
    return max
}

func main() {
    fmt.Printf("%d\n", maxGeneric([]int{1, 2, 3}))
    fmt.Printf("%s\n", maxGeneric([]string{"a", "b", "c"}))
    fmt.Printf("%s\n", maxGeneric([]mystring{"a", "b", "c", "d"}))
}

测试

Testing包

  1. 文件名以 _test 结尾
  2. 支持单元测试与性能基准测试

单元测试

  1. 单元测试方法以 Test 开头
  2. 使用命令 go test 执行
1
2
3
4
5
package hellotest

func TestHello(t *testing.T) {
    t.Log("this is a test")
}

性能基准测试

  1. 性能基准测试以 Benchmark 开头
  2. 使用 go test -bench=. 执行
1
2
3
4
5
6
7
package hellotest

func BenchmarkHello(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Printf("hello")
    }
}

常用GO命令

  • go version
  • go build
  • go run
  • go install
  • go mod
    • go mod init
    • go mod tidy
  • go doc
  • go work
  • go help

常用内建包

  • fmt
  • strings
  • strconv
  • time
  • os
  • errors

最佳实践

error 类型返回值放在返回值列表的末尾

$GOPATH/src/net/http/server.go

接收接口,返回结构体

1
2
3
4
// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
    return &Cond{L: l}
}

$GOPATH/src/sync/cond.go

发现接口而不是提前进行抽象

非必要不抽象

泛型的使用

  • 类型参数名的首字母通常采用大写形式,并且类型参数必须是具名的
  • 使用类型参数的原因是它们让你的代码更清晰,如果它们会让你的代码变得更复杂,就不要使用

REF

https://blog.go-zh.org/gos-declaration-syntax

https://colobu.com/

END