一.Go 语言介绍

Go 即 Golang,是 Google 公司 2009 年 11 月正式对外公开的一门编程语言。根据 Go 语言开发者自述,近 10 多年,从单机时代的 C 语言到现在互联网时代的Java,都没有令人满意的开发语言,而 C++往往给人的感觉是,花了 100%的经历,却只有60%的开发效率,产出比太低,Java 和 C#的哲学又来源于 C++。并且,随着硬件的不断升级,这些语言不能充分的利用硬件及 CPU。因此,一门高效、简洁、开源的语言诞生了

Go 语言不仅拥有静态编译语言的安全和高性能,而且又达到了动态语言开发速度和易维护性。有人形容 Go 语言:Go = C + Python , 说明 Go 语言既有 C 语言程序的运行速度,又能达到 Python 语言的快速开发

Go 语言是非常有潜力的语言,是因为它的应用场景是目前互联网非常热门的几个领域,比如 WEB 开发、区块链开发、大型游戏服务端开发、分布式/云计算开发。国内比较知名的B 站就是用 Go 语言开发的,像 Goggle、阿里、京东、百度、腾讯、小米、360 的很多应用也是使用 Go 语言开发的。


二、基础语法部分

1.定义变量

// var 变量名 = 表达式
var a = "123"
fmt.Println(a)

// 变量名 := 表达式
b := 20
fmt.Printf("%v %T\n",b,b)

2.一次性声明多个变量

// var ( 变量名 = 表达式 ... )
var (
	name = "ww"
	age = 33
	sex = "男"
)
fmt.Println(name, age, sex)

// 变量名,变量名 := 表达式,表达式
name, age, sex := "zz", 30, "女"
fmt.Println(name, age, sex)

3.定义常量

const pi = 3.1415
fmt.Println(pi)

const  (
	n1 = 100 + iota // 100 + 0
	_
	n3
)
fmt.Println(n1,n3) // 100 102

4.基本类型 整型、浮点型、布尔型、字符串

a.整型

var a uint8 = 128 // 1字节
var b int8 = -2 // 1字节
fmt.Println(int8(a)+b) // 126

c: = 1 // 64位,8字节  32位,4字节

b.浮点型和布尔型

var a float32 = 3.12
fmt.Println(unsafe.Sizeof(a)) // 4字节

var b float64 = 3.124323544
fmt.Println(unsafe.Sizeof(b)) // 8字节

c := 3.12
fmt.Println(unsafe.Sizeof(c)) // 64位,8字节  32位,4字节

var flag = true
fmt.Printf("%v %T\n", flag, flag)
if flag {
	fmt.Println("ok")
} else {
	fmt.Println("no")
}

c.字符串 声明

str := "\"Hello World!\""
str2 := "C:\\Go\\bin\n"
str3 := `
Hello
Word
Yeah
`
fmt.Println(str,str2, str3)

长度

str1 := "中" // 3个字节
str2 := "国"
fmt.Println(str1, len(str1),str1 + str2)
str3 := fmt.Sprintf("%v %v",str1,str2) // 拼接
fmt.Println(str3)

Split和Join

arr :=[]string{"ts","java","go"}
strs := strings.Join(arr, "-")
fmt.Println(strs)
fmt.Println(strings.Split(strs, "-"))

Contains、HasPrefix、HasSuffix

// 包含,前缀,后缀
strs := "Hello,World!"
flag := strings.Contains(strs, "Wo")
flag2 := strings.HasPrefix(strs, "He")
flag3 := strings.HasSuffix(strs, "!")
if flag && flag2 && flag3 {
	fmt.Println("Have")
}

Index和LastIndex

strs := "Hello,World!"
fmt.Println(strings.Index(strs, "o")) // 4
fmt.Println(strings.LastIndex(strs, "o")) // 7
fmt.Println(strings.LastIndex(strs, "-")) // -1

d.字符

// 字符 byte字母ASCII编码 rune汉字UTF-8编码
a := '中'
fmt.Printf("%v %c %T\n",a,a,a) // 20013 中 int32

b :="中国"
fmt.Printf("%v %T\n",b,b) // 中国 string

fmt.Println(unsafe.Sizeof(b),len(b)) // 16 6字节

c :="你好, GO"
for _, v := range c { // rune
	fmt.Printf(("%c\n"), v)
}

类型转换 a.int/float 转 string

// 方式一:fmt.Sprintf
i := fmt.Sprintf("%v", 123)
f := fmt.Sprintf("%v", 1.23566)

fmt.Printf("%s %T\n",i,i) // int to string
fmt.Printf("%s %T\n",f,f) // float to string

// 方式二:strcov
i2 := strconv.Itoa(123)
f2 := strconv.FormatFloat(1.23566, 'f', -1, 64)

fmt.Printf("%s %T\n",i2,i2) // int to string
fmt.Printf("%s %T\n",f2,f2) // float to string

b.string 转 int/float

s := "123"
i, _ := strconv.Atoi(s)
i2, _ := strconv.ParseInt(s, 10, 0)
fmt.Printf("%v %T\n",i,i) // string to int
fmt.Printf("%v %T\n",i2,i2) // string to int64

s2 := "1.23566"
f, _ := strconv.ParseFloat(s2, 64)
fmt.Printf("%v %T\n",f,f) // string to float64

三.内置运算符

算术运算符、关系运算符、逻辑运算符、赋值运算符、位运算符

// 算术运算符 + - * / %
fmt.Println(3+2)
fmt.Println(3-2)
fmt.Println(3*2)
fmt.Println(3.0/2)
fmt.Println(3%2)

// 关系运算符 ==、!=、>、>=、<、<=
var (
	a1 = 9
	a2 = 8
	a3 = true
)
fmt.Println(a1 >= a2) // true

// 逻辑运算符  && ||  !
if a1 > 9 && a2 < 9 {
	println("&&")
}
if a1 > 9 || a2 < 9 {
	println("||")
}
if(!a3) {
	println(('!'))
}

// 赋值运算符 =、+=、-=、*=、/=、%=
var a = 1
a += 3
println(a) // 4
a *= 4
println(a) // 16
a /= 2
println(a) // 8
a %= 4
println(a) // 0

// 位运算符 &、|、^、<<、>>
var a = 5 // 二进制 101
var b = 2 // 二进制 010
fmt.Println("a&b=", a&b) // 000 值0
fmt.Println("a|b=", a|b) // 111 值7
fmt.Println("a^b=", a^b) // 111 值7
fmt.Println("a<<b=", a<<b) // 10100 值20
fmt.Println("a>>b=", a>>b) // 5/2的2次方 值1

四.流程控制

1.if else

// 全局变量
flag := true
if flag {
	fmt.Println("flag")
}

// 局部变量
if age := 20; age > 40 {
	fmt.Println("> 40")
} else if age > 20 {
	fmt.Println("> 20")
} else {
	fmt.Println("<= 20")
}

2.for

/**
for 初始语句;条件表达式;结束语句{
    循环体语句
}
**/

// 基本
for i := 0; i < 10; i++ {
	fmt.Println(i) // 0 ~ 9
}

// 条件
i := 0
for i < 10 {
	fmt.Println(i) // 0 ~ 9
	i++
}

// 类似 while 循环
for {
	if i < 10  {
		fmt.Println(i)
	} else {
		break
	}
	i++
}

// 九九乘法表
for i := 1; i <= 9; i++ {
	for j := 1; j <= i; j++ {
		fmt.Printf("%v*%v=%v \t",j,i,i*j)
	}
	fmt.Println("")
}

3.for range 可以使用 for range 遍历数组、切片、字符串、map 及通道(channel)

// 字符串
str := "你好,世界!"
for _,v := range str {
	fmt.Printf("%c\n",v)
}
// 切片
arr :=[]string{"ts","java","go"}
for _,v := range arr {
	fmt.Printf("%s\n",v)
}

4.switch case

// break可以不写
switch n := 1;n {
case 1, 3, 5, 7, 9:
	fmt.Println("奇数")
case 2, 4, 6, 8:
	fmt.Println("偶数")
default:
	fmt.Println(n)
}

5.其他

// fallthrough 穿透,默认只穿透一层(用于switch)
// break 跳出循环(用于for 或者 switch)
// continue 跳过当前循环,继续下次循环(用于for)
// goto 跳转到指定标签

var n = 30
if n > 24 {
	fmt.Println("成年人")
	goto x
}
fmt.Println("aaa")
fmt.Println("bbb")
x:
fmt.Println("ccc")

// 成年人、ccc

五.数组Array和切片Slice

数组(Array)是指一系列同一类型数据的集合,值类型,长度固定

切片 (Slice)是指一个拥有相同类型元素的可变长度的序列,引用类型,长度不固定。它非常灵活,支持自动扩容。

// 声明一维数组
var arr1 = [3]int{22, 24, 5}
var arr2 = [3]string{"ts", "java", "go"}
// 值类型:数组
var arr3 = [...]int{1,4,6,6}
// 引用类型:切片
var arr4 = []int{1,4,6,6}

var a = arr3
arr3[0] = 2
var b = arr4
arr4[0] = 2
fmt.Println(arr3, a) // [2 4 6 6] [1 4 6 6]
fmt.Println(arr4, b) // [2 4 6 6] [2 4 6 6]
fmt.Println(arr2)
fmt.Println(arr3, len(arr3))
// 遍历
for _, v := range arr1 {
	fmt.Println(v)
}

// 声明二维数组
var arr5 = [3][2]string {
	{"A1","B1"},
	{"B2","B2"},
	{"C3","C2"},
}
// 遍历
for _, v := range arr5 {
	for _,j := range v {
		fmt.Println(j)
	}
}

切片的本质就是对底层数组的封装, 它包含了三个信息: 底层数组的指针、 切片的长度 (len) 和切片的容量(cap)

// 长度:包含的元素个数
// 容量:从它第一个元素开始数,到其底层元素末尾的个数
var arr1  []int
var arr2  = []int{1,2,3}
var arr3 = arr2[1:2]
fmt.Println(arr1 == nil, arr2 == nil) // true false
fmt.Println(arr1, len(arr1), cap(arr1)) // [] 0 0
fmt.Println(arr2, len(arr2), cap(arr2)) // [1 2 3] 3 3
fmt.Println(arr3, len(arr3), cap(arr3)) // [2] 1 2
// make() 函数创建一个切片 make([]T, size, cap)
sliceA := make([]int, 4, 4)
fmt.Println(sliceA,len(sliceA),cap(sliceA))
// 切片修改
sliceA[0] = -1
// 切片新增
sliceA = append(sliceA, 1,2,3,4,5)
// 切片删除
sliceD := append(sliceA[:1],sliceA[4:]...)
fmt.Println(sliceA,len(sliceA),cap(sliceA))
fmt.Println(sliceD,len(sliceD),cap(sliceD))

// 切片合并
arr1 := []string{"a","b"}
arr2 := []string{"c","d"}
arr3 := append(arr1, arr2...)
fmt.Println(arr3)

// copy引用类型
arr4 := []int{1,2}
arr5 := make([]int,2,2)
copy(arr5, arr4)
arr5[1] = 3
fmt.Println(arr4, arr5)

六.字典Map

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

// 声明
user := map[string]string{
	"name": "ww",
	"age": "33",
	"sex": "male",
}
fmt.Println(user) // map[age:33 name:ww sex:male]

// 遍历
for k,v := range user {
	fmt.Println(k,v)
}

// 新增
user["hobby"] = "play"
// 获取
v, ok := user["name"]
fmt.Println(ok, v)
// 修改
user["name"] = "zz"
fmt.Println(user) // map[age:33 hobby:play name:zz sex:male]
// 删除
delete(user, "hobby")
fmt.Println(user) // map[age:33 name:zz sex:male]

// 声明一个赋值string[]
ww := make(map[string][]string)
ww["work"] = []string{
	"eat",
	"coding",
	"sleep",
}
fmt.Println(ww) // map[work:[eat coding sleep]]

七.函数Func

Go 语言中定义函数使用 func 关键字,具体格式如下:

func 函数名(参数)(返回值){
    函数体
}

可变参数是指函数的参数数量不固定。Go 语言中的可变参数通过在参数名后加...来标识。

var global = "全局变量"

// 求和
func sum(x int, y int) int {
	return  x + y
}

// 求差
func sub(x int, y int) int {
	return x - y
}

// 合并
func clac1(x int, y int) (int, int) {
	return  x + y, x - y
}

func clac2(x, y int) (sum int, sub int) {
	fmt.Println(global)
	sum = x + y
	sub = x - y
	return
}

func main() {
	/*
	全局变量:函数外部
	局部变量:函数内部
	*/

	a, b := clac1(2,3)
	c, d := clac2(2,3)
	fmt.Println(sum(2,3), sub(2, 3), a,b,c,d) // 5 -1 5 -1 5 -1
}

2.定义函数类型 我们可以使用 type 关键字来定义一个函数类型

type clacType func (int, int) int //表示定义了一个calc的类型
// 求差
func sub(x int, y int) int {
	return x - y
}
var e clacType = sub // 自定义类型: main.clac
f := sub // 推倒类型:func(int, int) int
fmt.Printf("%T %T\n", e, f) // main.clac func(int, int) int

3.高阶函数 高阶函数分为函数作为参数和函数作为返回值两部分。

func clac(x, y int, cb clacType) int {
	return  cb(x, y)
}
fmt.Println(clac(2, 3, sum), clac(2, 3, sub)) // 5 -1

4.匿名函数和闭包 匿名函数就是没有函数名的函数,闭包可以理解成 “定义在一个函数内部的函数“。 本质上, 闭包是将函数内部和函数外部连接起来的桥梁

type clacType func (int, int) any

func do(t string) clacType {
	switch t {
		case "+":
		return  func(x, y int) any {
			return  x + y
		}
	case "-":
		return  func(x, y int) any {
			return  x - y
		}
	case "*":
		return  func(x, y int) any {
			return  x * y
		}
	case "/":
		return  func(x, y int) any {
			return  float64(x) / float64(y)
		}
	default:
		return nil
	}

}

fmt.Println(do("+")(2,3), do("-")(2,3), do("*")(2,3), do("/")(2,3))
// 5 -1 6 0.6666666666666666

5.柯里化函数 柯里化(Currying)是一种函数式编程技术: 接受多个参数的函数转换为一系列接受单个参数的函数,通过闭包,每个函数都可以访问之前传入的参数。 柯里化函数优势:

  1. 函数复用:可以创建预配置的函数
  2. 代码组织:将复杂函数拆分为多个步骤
  3. 灵活性:可以按需组合不同的函数
  4. 可读性:代码更清晰,意图更明确"
// 普通加法函数
func add(a, b int) int {
	return a + b
}

// 柯里化版本:先传入第一个参数,返回一个函数
func curryAdd(x int) func(int) int {
	// 闭包捕获了 x
	return func(y int) int {
		return x + y
	}
}
add(2,3) // 5
curryAdd(2)(3) // 5

八.报错和时间

1.defer/panic/recover defer 语句会将其后面跟随的语句进行延迟处理。 使用 panic/recover 模式来处理错误。

defer fmt.Println("World")
fmt.Println("Hello")
// Hello World
safeOperation()

func safeOperation() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("  捕获panic: %v\n", r)
			fmt.Println("  程序继续执行")
		}
	}()

	fmt.Println("  即将触发panic")
	panic("发生错误")
	fmt.Println("  这行不会执行")
}

2.time

now := time.Now()
fmt.Println("当前时间:", now)
fmt.Println("当前时间戳:", now.Unix())
// 格式化的模板为 Go 的出生时间 2006 年 1 月 2 号 15 点 04 分,2006 1,2,3,4,5
var tmp = "2006-01-02 15:04:05"
fmt.Println("当前日期字符串:", now.Format(tmp))
// 时间转换
fmt.Println("时间戳转日期字符串",time.Unix(1789034269, 0).Format(tmp))
t, _ :=time.ParseInLocation(tmp,"2026-09-10 17:57:49",time.Local)
fmt.Println("日期字符串转时间戳",t.Unix())
// 时间函数
fmt.Println("当前时间加1小时", now.Add(time.Hour).Format("15:04"))
// 设置定时器,每隔1s
ticker := time.NewTicker(time.Second) // 定义1s的定时器
n := 1
for i := range ticker.C {
	fmt.Println("定时执行任务:"+i.Format("04:05"))
	n++
	if n > 5 {
		ticker.Stop()
		return
	}
}
for {
	time.Sleep(time.Second)
	fmt.Println("定时执行任务")
}

九.指针

指针也是一个变量, 但它是一种特殊的变量, 它存储的数据不是一个普通的值, 而是另一个变量的内存地址。 Go 语言中的指针操作非常简单,我们只需要记住两个符号:&(取内存地址)和 *(根据内存地址取值)

a := 10
b := &a // 获取a的内存地址
fmt.Printf("a:%v type:%T ptr:%p\n", a, a, &a) // a:10 type:int ptr:0x4230641ea0c0
fmt.Printf("b:%v type:%T ptr:%p\n", *b, b, b) // b:0x4230641ea0c0 type:*int ptr:0x4230641ea0c0

// make、new 分配内存空间
var user = make(map[string]string)
user["name"] = "ww"
fmt.Println(user)

var arr = make([]int, 4, 4)
arr[0] = 1
fmt.Println(arr)

// 了解即可
var c = new(int)
*c = 100
fmt.Println(*c)

十.结构体

1.Golang 提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。

/*
值类型:数组,基本数据结构,结构体
引用类型:切片,map

定义结构体:
type 类型名 struct {
	字段名 字段类型
	字段名 字段类型
	…
}
*/
type Person struct {
	name string
	age int
	sex string
}
/*
定义结构体方法:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
	函数体
}
*/
func (p Person) PrintInfo() {
	fmt.Printf("姓名:%v 年龄:%v\n",p.name, p.age)
}
/* 必须靠指针修改值 */
func (p *Person) SetInfo(name string, age int) {
	p.name = name
	p.age = age
}

// 实例化,常用 p4
var p1 Person
p2 := new(Person)
p3 := &Person{
	name: "default",
}
p4 := Person {
	name: "ww",
	age: 33,
}
p3.SetInfo("zz", 20)
p3.PrintInfo()
p4.PrintInfo()
fmt.Println(p1, *p2, *p3, p4)

2.结构体的嵌套

type Person struct {
	name string
	age int
	hobby []string
	record map[string]string
	address Address
}

type Address struct {
	city string
	phone string
}

func (p Person) PrintInfo() {
	fmt.Printf("姓名:%v 年龄:%v\n",p.name, p.age)
}
func (p *Person) SetInfo(name string, age int) {
	// 必须靠指针修改
	p.name = name
	p.age = age
}
func (p *Person) SetHobby(hobby []string) {
	p.hobby = hobby
}
func (p *Person) SetRecord(record map[string]string) {
	p.record = record
}
func (p *Person) SetAddress(address Address) {
	p.address = address
}

p := Person {}
p.SetInfo("zz", 20)
p.SetHobby([]string{"eat","coding","sleep"})
p.SetRecord(map[string]string{
	"1": "200",
	"2": "300",
})
p.SetAddress(Address{city:"shanghai", phone: "136xxxxx666"})
fmt.Println(p) // {zz 20 [eat coding sleep] map[1:200 2:300] {shanghai 136xxxxx666}}

3.结构体的继承

type Animal struct {
	name string
}

func (a Animal) eat (){
	fmt.Printf("%v 在吃饭\n", a.name)
}

type Person struct {
    age int
	Animal // 继承Animal
}
p := Person {
    name: "zz",
    age: 20
}
p.eat()
fmt.Println(p) // {20 {zz}}

4.结构体对象和Json字符串之间的转行

// Marshal 和 Unmarshal
// 通过`json:"name"`指定tag实现json序列化的key
type People struct {
	Age int `json:"age"`
	Animal
}

type Animal struct {
	Name string `json:"name"`
}

p := People {
	Name: "zz",
	Age: 20,
}

// 结构体 to json string
jsonByte, _ := json.Marshal(p)
fmt.Println(string(jsonByte)) // {"age":20,"name":"zz"}

// json string to 结构体
str := `{"Age":33,"Name":"ww"}`
p2 := People {}
err := json.Unmarshal([]byte(str), &p2)
if err != nil {
	fmt.Println("error")
} else {
    fmt.Printf("%v\n",p2) // {33 {ww}}
}

结构体嵌套结构体

type Student struct {
	ID     int
	Gender string
	Name   string
}

type Class struct {
	Title    string
	Students []Student
}

c := &Class{
	Title:    "001",
	Students: make([]Student, 0, 200),
}
for i := 0; i < 2; i++ {
    // 添加学生
	stu := Student{
		Name:   fmt.Sprintf("stu%02d", i),
		Gender: "男",
		ID:     i,
	}
	c.Students = append(c.Students, stu)
}
fmt.Println(*c) // {001 [{0 男 stu00} {1 男 stu01}]}


str =
`{
"Title":"001",
"Students":[{
	"ID":0,
	"Gender":" 男",
	"Name":"stu00"
},{
	"ID":1,
	"Gender":"男",
	"Name":"stu01"
}]
}`
c1 := &Class{}
json.Unmarshal([]byte(str), c1)
fmt.Printf("%#v\n", *c1)
// main.Class{Title:"001", Students:[]main.Student{main.Student{ID:0, Gender:" 男", Name:"stu00"}, main.Student{ID:1, Gender:"男", Name:"stu01"}}}

十一.包管理工具 go mod

// 初始化一个新的 module
god mod init 项目名

// 定义一个包
package 包名

// 1.go内置方法和包
https://pkg.go.dev/std

// 2.初始化第三方包
go get github.com/shopspring/decimal

// 3.下载依赖第三方包
go mod tidy

目录结构如下:

demo
    |- clac
        |- clac.go
    |- tools
        |- a.go
        |- b.go
        |- c.go
    |- go.mod
    |- main.go

go.mod

module demo

go 1.27.0

main.go

package main

import (
	"fmt"
	"demo/calc"
	"demo/tools"
)

func init() {
	fmt.Println("main init")
}

func main() {
	fmt.Println("main")
	fmt.Println(calc.Add(2, 3), calc.Sum(2, 3), calc.Global)

	tools.A()
	tools.B()
	tools.C()

	/* 依次输出
    	calc init
        A init
        B init
        C init
        main init
        main

        5 -1 共有变量
        A
        B
        C
    */
}

clac.go

package calc

import "fmt"

var global = "私有变量"
var Global = "共有变量"

func init() {
	fmt.Println("calc init")
}

func Add(x, y int) int { // Add 共有办法 add 私有方法
	return x + y
}

func Sum(x, y int) int {
	return x - y
}

a.go

package tools

import "fmt"

func init() {
	fmt.Println("A init")
}

func A() {
	fmt.Println("A")
}

b.go

package tools

import "fmt"

func init() {
	fmt.Println("B init")
}

func B() {
	fmt.Println("B")
}

c.go

package tools

import "fmt"

func init() {
	fmt.Println("C init")
}

func C() {
	fmt.Println("C")
}

使用第三方包

import (
	"fmt"

	"github.com/shopspring/decimal"
	"github.com/tidwall/gjson"
)
func main() {
	price, err := decimal.NewFromString("136.11")
	if err != nil {
		panic(err)
	} else {
		fmt.Printf("%v  %T\n", price, price) // 136.11  decimal.Decimal
	}

	const json = `{"name":"ww","age":33,"hobby":["eat","coding","sleep"]}`
	println(gjson.Get(json, "hobby").String()) // ["eat","coding","sleep"]
}

十二.接口

// 接口是一个规范,统一设计的标准
type USB interface {
	start()
	stop()
}

// 手机
type Phone struct {
	Name string
}

// 接口有方法,通过结构体实现这个接口
func (p Phone) start() {
	fmt.Printf("%v开机\n", p.Name)
}

func (p Phone) stop() {
	fmt.Printf("%v关机\n", p.Name)
}

// 照相机
type Camera struct{}

func (p Camera) start() {
	fmt.Println("照相机开机")
}

func (p Camera) stop() {
	fmt.Println("照相机关机")
}

func (p Camera) run() {
	fmt.Println("照相机运行")
}

// 电脑
type Computer struct{}

func (c Computer) work(u USB) {
	u.stop()
	fmt.Println("连接电脑")
	u.start()
}

func main() {
	p := Phone{
		Name: "华为",
	}
	c := Camera{}
	c.run() // 照相机运行
	t := Computer{}
	t.work(p) // 华为关机 -> 连接电脑 -> 华为开机
	t.work(c) // 照相机开机 -> 连接电脑 -> 照相机关机
}

2.空接口和类型断言

// 空接口 表示没有任何约束,任意类型都可实现
type TempInterface interface{}

func main() {
	var a TempInterface = "123"
	var b TempInterface = true
	fmt.Println(a, b)
	// Go 1.18及以上,用any,1.18以下,用interface{}
	var c = []interface{}{"123", true, 12.04}
	var d = []any{"123", true, 12.04}
	fmt.Println(c, d)
	// 推荐
	var e = map[string]any{
		"name": "ww",
		"age":  33,
		"male": true,
	}
	fmt.Println(e)
	// 类型断言 判断空接口中值的类型 x.(T)
	v, ok := a.(string)
	if ok {
		fmt.Println(v) // 123
	} else {
		fmt.Println("error")
	}
}

func (c Computer) work(u USB) {
	// 断言,判断u类型
	if _, ok := u.(Phone); ok {
		fmt.Println("手机")
	} else {
		fmt.Println("相机")
	}
}

十三.结构体实现多接口

结构体接受者:接受值和接受指针的区别 接受指针,不能

type Animal interface {
	SetName(string)
	GetName() string
}

type Dog struct {
	Name string
}

func (d *Dog) SetName(name string) {
	d.Name = name
}

func (d Dog) GetName() string {
	return d.Name
}

var d Animal = &Dog{
	Name: "wangcai",
}
d.SetName("ww")
fmt.Println(d.GetName()) // 不能通过d.Name获取

结构体实现多个接口

type AInterface interface {
	GetName() string
}
type BInterface interface {
	SetName(string)
}
type Animal interface {
	AInterface
	BInterface
}

接口嵌套

type SayInterface interface {
    say()
}
type MoveInterface interface {
    move()
}
// 接口嵌套
type Animal interface {
    SayInterface
    MoveInterface
}

十四.协程

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

func test2() {
	for i := 0; i < 10; i++ {
		fmt.Println("主线程,golang-", i)
		time.Sleep(time.Millisecond * 50)
	}
}

func test1() {
    defer wg.Done() // 协程执行完毕
	// 开一个goroutine,该协程,每隔100毫秒,输出"你好,golang"
	for i := 0; i < 10; i++ {
		fmt.Println("协程,golang-", i)
		time.Sleep(time.Millisecond * 100)
	}
}

// 协程计数器
var wg sync.WaitGroup

func main() {
	/*
		进程:正在执行的程序
		线程:进程分配的多个任务
		并发:多个线程同时竞争一个位置,只有一个线程执行
		并行:多个线程可以同时执行
		单核CPU运行就是并发,多线程在多核CPU运行就是并行
		主线程(进程),可以起多个协程,线程(Java,C)2M,协程(Go)2KB
	*/
	fmt.Println("主线程---当前电脑CPU个数:", runtime.NumCPU())
	wg.Add(1)  // 协程计数器+1
	go test1() // 开启一个协程
	test2() // 主线程
	wg.Wait() // 等待协程执行完毕
}


十五.管道

// 管道创建
ch := make(chan int, 3)
// 管道存数据
ch <- 10
// 管道关闭
close(ch)
// 管道获取
fmt.Println(<-ch)
// 管道长度和容量
fmt.Printf("值:%v 容量:%v 长度:%v\n", ch, cap(ch), len(ch))

默认双向管道,声明只读<-chin,声明只写chin<-

// produce 函数只能向 ch 写入数据(只写通道)
func produce(ch chan<- int) {
	ch <- 10
	ch <- 12
	close(ch) // 写完记得关闭
}

// consume 函数只能从 ch 读取数据(只读通道)
func consume(ch <-chan int) {
	for num := range ch {
		fmt.Println(num)
	}
}

// 1. 在 main 中声明一个正常的“双向通道”
ch := make(chan int, 2)

// 2. 传递给函数时,会自动隐式转换为单向通道
go produce(ch)
consume(ch)

select多路复用,如果通道缓冲区满了,或者没有协程在接收,正常写入会阻塞。

// 创建一个容量为 1 的有缓冲通道
ch := make(chan int, 1)

ch <- 100 // 此时通道满了

// 再次尝试非阻塞写入
select {
case ch <- 200:
	fmt.Println("成功写入 200")
default:
	// 如果通道满了,立刻走到这里,不会阻塞
	fmt.Println("通道已满,写入失败(避免了死锁/阻塞)")
}

十六.并发安全和锁

互斥锁是传统并发编程中对共享资源进行访问控制的主要手段, 它由标准库 sync 中的 Mutex 结构体类型表示。sync.Mutex 类型只有两个公开的指针方法,Lock 和 Unlock。Lock 锁定当 前的共享资源,Unlock 进行解锁。1.互斥锁(Mutex)

// 两个协程同时去加同一个数字。如果不加锁,数字会错乱;加了锁,结果就绝对正确。
var count = 0
var mu sync.Mutex // 1. 声明互斥锁

// 协程 A:加 1000 次
go func() {
	for i := 0; i < 1000; i++ {
		mu.Lock()   // 2. 加锁
		count++     // 3. 修改公共变量
		mu.Unlock() // 4. 解锁
	}
}()

// 协程 B:加 1000 次
go func() {
	for i := 0; i < 1000; i++ {
		mu.Lock()   // 2. 加锁
		count++     // 3. 修改公共变量
		mu.Unlock() // 4. 解锁
	}
}()

time.Sleep(time.Second) // 等待两个协程执行完毕
fmt.Println("最终结果:", count) // 必定输出 2000

2.读写锁(RWMutex)

var score = 100
var rwMu sync.RWMutex // 1. 声明读写锁

// 协程 1:读取数据(用 RLock)
go func() {
	rwMu.RLock() // 加读锁
	fmt.Println("读协程 1 看到分数:", score)
	rwMu.RUnlock()
}()

// 协程 2:读取数据(用 RLock,可以和协程 1 同时读)
go func() {
	rwMu.RLock() // 加读锁
	fmt.Println("读协程 2 看到分数:", score)
	rwMu.RUnlock()
}()

// 协程 3:修改数据(用 Lock,排斥所有人)
go func() {
	rwMu.Lock() // 加写锁
	score = 200
	fmt.Println("写协程 修改分数完毕")
	rwMu.Unlock()
}()

time.Sleep(time.Second) // 等待所有协程执行完

十七.反射

ORM框架,用的就是反射机制。 ORM:对象关系映射(Object Relational Mapping,简称 ORM)是通过使用描述对象和数据库 之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。

num := 100
// 1. 获取类型 (TypeOf)
t := reflect.TypeOf(num)
fmt.Println("类型是:", t) // 输出: int

// 2. 获取值 (ValueOf)
v := reflect.ValueOf(num)
fmt.Println("值是:", v) // 输出: 100

进阶,类型名字t.Name(),底层种类t.Kind()

u := User{Name: "ww"}
// ======= 1. reflect.TypeOf 进阶 =======
t := reflect.TypeOf(u)

// t.Name() 是你定义的类型名字 -> "User"
// t.Kind() 是 Go 底层认定的种类 -> "struct"
fmt.Println("类型名字(Name):", t.Name())
fmt.Println("底层种类(Kind):", t.Kind())

// ======= 2. reflect.ValueOf 进阶 =======
v := reflect.ValueOf(u)

// 拿到值对象 v 之后,可以通过它的接口把值“还原”或“提取”出来
fmt.Println("直接打印值(ValueOf):", v) // 输出: {ww}

// 提取出真正的底层类型
realString := v.Field(0).String()   // 获取结构体第一个字段并转为 string
fmt.Println("提取出字段的值:", realString) // 输出: ww

// 还原回普通变量 (Interface)
originalData := v.Interface().(User)       // 还原并断言回 User 类型
fmt.Println("还原后的结构体:", originalData.Name) // 输出: ww

// 不要乱用反射,大量使用反射的代码通常难以理解。