简介
- GO诞生于2009年,官网
- 静态编译型语言
- 强类型语言

创始人
- Ken Thompson
- Rob Pike
- Robert Griesemer

- 肯 汤普森
- 罗伯 派克
- 罗伯特 格瑞史莫
目标
- 简单
- 高效
- 生产力
借鉴多门编程语言,新时代的C
设计哲学
- 简单
- 显式
- 组合
- 并发
- 面向工程
类型
普通类型
$GOPATH/src/builtin/builtin.go
type-study/type-study_test.go
https://www.educative.io/answers/what-is-type-uintptr-in-golang
int
- int
- int8
- int16
- int32
- int64
uint
- uint
- uint8
- uint16
- uint32
- uint64
- uintptr
不带数字的为平台相关的类型
float
- float32
- float64
bool
- true
- false
var a bool = true
var b bool = false
string
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
int a = 1;
long b = a;
GO显示转换
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)
自定义类型
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)
类型别名
- go 1.9加入
- 在大规模的重构项目代码的时候
- 支持为其它包定义别名
- 少写变量
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)
类型别名与类型定义的区别
- 类型别名和原类型是相同的
- 类型定义和原类型是不同的两个类型
type S = string
type MyString string
var s1 S
var s2 MyString
fmt.Printf("%T\n", s1)
fmt.Printf("%T\n", s2)
集合
数组
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
切片
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引入
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
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")
指针
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
type User struct {
Name string
Age int
Address string
}
嵌入结构体
type Address struct {
Name string
Code string
}
type User struct {
Name string
Age int
Address Address
}
使用结构体
type User struct {
Name string
Age int
Address string
}
var user User
fmt.Println(user)
user1 := User{
Name: "John",
Age: 1,
}
fmt.Println(user1)
空结构体
type User struct{}
常用作事件消息通信
接口类型
空接口
var i interface{}
i = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)
var j any // add from 1.18
fmt.Println(j)
自定义接口
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()
}
零值可用
可以直接在声明变量上进行调用
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()
- 不用担心变量的初始化状态
变量
作用域
- 全局变量
- 局部变量
声明
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
常量
const lang = "english"
const (
JAVA = "java"
GO = "go"
)
const ua = 10
var b int64 = 100
fmt.Println(ua + b)
无类型常量,隐式转型
枚举
- 值从0开始
- 递增1
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
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
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
- switch支持多种比较对象,只要类型支持比较操作
- 支持声明临时变量
- 支持表达式列表
- 取消默认执行下一句,想继续执行要使用 fallthrough
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
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必须为接口类型
函数
一等公民
- 声明为变量
- 做为参数
- 做为返回值
main()
- 程序的入口
- 无参数
- 无返回值
- main包包名要为main且只可有一个main函数
init()
- 先于main函数执行
- 多个按代码顺序执行
运行顺序: 常量->变量->init()
init()用途
- 用于修改包级变量
- 包复杂变量的初始化
- 注册模式
trpc-go的config
可变参数
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)
}
底层实现为数组
多返回值
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
面向对象编程
方法
- 必须属于某一个类型
- T与*T的方法可互调
- go不支持方法重载
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())
}
方法表达式
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参数作为第一个参数的普通函数
继承
- 不支持继承,支持复合
- 匿名类型嵌入
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如何实现继承
接口
- Duck Type式
- 可定义非导出方法
定义与实现
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)
判断接口类型
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为真正的类型
空接口
var i interface{}
// go 1.18
// var i any
fmt.Println(i)
i = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)
错误与异常
- 错误统一用error代表
- 异常使用panic()
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
- 在go运行时触发
- 通过 panic() 触发
- 导致程序崩溃
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
异常恢复
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
- 用于资源清理
- 定义的方法执行时先进后出
- defer代码执行时,参数就会被绑定
- defer可以读取命名返回值
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 包下
- Mutex 互斥锁
- RWMutex 互斥读写锁
- WaitGroup 等待多个任务完成
- Cond 条件变量
- Map 并发安全的Map
- Once 只执行一次
Channel
用于goroutine之间通信
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 新特性
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包
- 文件名以 _test 结尾
- 支持单元测试与性能基准测试
单元测试
- 单元测试方法以 Test 开头
- 使用命令 go test 执行
package hellotest
func TestHello(t *testing.T) {
t.Log("this is a test")
}
性能基准测试
- 性能基准测试以 Benchmark 开头
- 使用 go test -bench=. 执行
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
接收接口,返回结构体
// 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