GO
变量定义与输入输出
变量定义
package main
import "fmt"
func main() {
//先申明 (必须指定类型)
var name string
//再赋值
name = "test"
fmt.Println(name)
//申明并赋值
var name1 = "test2"
//省略类型
var name2 string = "test3"
fmt.Println(name1, name2)
//短申明
name3 := "test4"
fmt.Println(name3)
}
局部变量和全局变量
package main
import "fmt"
//全局变量必须使用var
var age = 2
func main() {
//全局变量可以不使用
fmt.Println(age)
}
定义多个变量
package main
import "fmt"
var (
s1 string = "1"
s2 string = "2"
)
func main() {
var a1, a2 = 1, 2
fmt.Println(a1, a2)
}
常量定义
package main
import "fmt"
//常量定义必须赋值
const version = "1.0"
func main() {
fmt.Println(version)
}
跨包访问
目录结构
C:.
│ GO.md
│ go.mod
│
├─.vscode
│ launch.json
│
└─Variable
│ main.go
│
└─test
test.go
package test
//变量名必须大写
const Version string = "1.0.2"
const name = "aaa"
package main
import (
"demo/Variable/test"
"fmt"
)
func main() {
fmt.Println(test.Version)
}
输出
| 函数 | 换行 | 自动加空格 | 支持格式化 | 示例输出 |
|---|---|---|---|---|
Print() |
❌ 不换行 | ❌ 不加 | ❌ | helloworld |
Println() |
✅ 换行 | ✅ 加空格 | ❌ | hello world |
Printf() |
❌ 不换行 | ❌ 不加 | ✅ | name: 张三 |
| 动词 | 作用 | 示例 |
|---|---|---|
%v |
默认格式 | [1 2 3] |
%+v |
带字段名 | {Name:张三 Age:25} |
%#v |
Go 语法 | []int{1,2,3} |
%T |
类型 | []int |
%d |
十进制整数 | 100 |
%s |
字符串 | "hello" |
%q |
带引号字符串 | "hello" |
%p |
打印内存地址 | 0xc0000180a8 |
package main
import "fmt"
func main() {
//println输出有换行,打印多个值会空格
fmt.Println("aa")
//格式化输出
fmt.Printf("%s嗯?\n", "搞什么")
fmt.Printf("%s嗯?\n", "搞什么.")
//保留整数
fmt.Printf("%d\n", 123)
//保留整数
fmt.Printf("%.2f\n", 123.123)
//打印类型
fmt.Printf("%T %T\n", "cao", 123.123)
//默认
fmt.Printf("%v \n", "")
fmt.Printf("%#v \n", "")
var f = fmt.Sprintf("%T %T\n", "cao", 123.123)
fmt.Printf(f)
}
- 输出
aa
搞什么嗯?
搞什么.嗯?
123
123.12
string float64
""
string float64
输入
package main
import "fmt"
func main() {
fmt.Printf(":")
var name string
fmt.Scan(&name)
fmt.Printf(name)
}
基本数据类型
整数型
- 默认的数字定义类型是int
- 带u就是无符号,只能存正整数
- 后面数字就是二进制的位数
- uint8还有别名byte,1字节等于8bit
- 不带数字的int大小取决于使用的平台
package main
import "fmt"
func main() {
var age = 12
var ua8 uint8 = 255
var a8 int8 = -127
fmt.Printf("%T\n", age)
fmt.Printf("%T\n", ua8)
fmt.Printf("%T\n", a8)
}
- 输出
int
uint8
int8
浮点型
go支持两种,如果不申明,默认是float64
- float32
- float64
字符型
重要的是byte(单字节)和rune(多字节)
''(单引号) 专门用来存储 单个字符,byte和rune都可以用
- byte
package main
import "fmt"
func main() {
var a byte = 'a' //ascii 字符
fmt.Printf("%c %d\n", a, a)
var a1 uint8 = 97
fmt.Printf("%c %d\n", a1, a1)
}
- rune
package main
import "fmt"
func main() {
var z rune = '中'
fmt.Printf("%c %d\n", z, z)
}
字符串
和字符不一样,字符的赋值是单引号,字符串是双引号
package main
import "fmt"
func main() {
fmt.Printf("wo\tcao\n") //制表符
fmt.Printf("'wo'cao\n")
fmt.Printf("\"wocao\"cao\n")
fmt.Printf("wo\rcao\n") // 回到首行
}
//\r使用查找
func main() {
// 进度条效果
for i := 0; i <= 100; i += 10 {
fmt.Printf("\r加载中... %d%%", i)
time.Sleep(200 * time.Millisecond)
}
fmt.Println("\n完成!")
}
多行字符
package main
import "fmt"
func main() {
//会原样输出,与转义字符不兼容
fmt.Println(`
wocao \n
wocaocao
`)
}
- 输出
wocao \n
wocaocao
布尔类型
- 布尔型的变量默认值为false
- 不允许将整型转换为布尔型
- 布尔型无法参与数值运算,也无法与其他类型转换
零值问题
如果给一个基本的数据类型只声明不赋值
那么这个变量的值就是对应类型的零值,init就是0,bool就是false,字符串就是“”
package main
import "fmt"
func main() {
var a1 int
var a2 float32
var a3 bool
var a4 string
fmt.Printf("%#v\n", a1)
fmt.Printf("%#v\n", a2)
fmt.Printf("%#v\n", a3)
fmt.Printf("%#v\n", a4)
}
- 输出
0
0
false
""
数组、切片、map
数组
package main
import (
"fmt"
)
func main() {
//go不支持负向索引
var nameList = [3]string{"1", "2", "3"}
fmt.Println(nameList[0])
fmt.Printf("%v", nameList)
fmt.Println(nameList)
fmt.Println(nameList[len(nameList)-1])
nameList[0] = "4"
fmt.Println(0)
}
- 输出
1
[1 2 3]
[1 2 3]
3
0
切片
go中的数组长度被限死了,声明后无法添加
切片更加灵活
package main
import (
"fmt"
)
func main() {
var nameList []string
nameList = append(nameList, "aa")
nameList = append(nameList, "bb")
fmt.Println(nameList[0])
}
- 创建空切片
package main
import "fmt"
func main() {
// 四种创建空切片的方式
var nameList []string = []string{} // 方式1:显式类型
var nameList1 = []string{} // 方式2:类型推断
nameList2 := []string{} // 方式3:短变量声明
nameList3 := make([]string, 0) // 方式4:make 0是长度
// 检查是否为 nil
fmt.Println(nameList == nil) // false
fmt.Println(nameList1 == nil) // false
fmt.Println(nameList2 == nil) // false
fmt.Println(nameList3 == nil) // false
}
- 切片
package main
import "fmt"
func main() {
//如果设置 [3],会变成数组(array)而不是切片(slice)
array := []string{"1", "2", "3"}
aa := array[:]
fmt.Println(aa)
fmt.Println(aa[0:2])
fmt.Println(aa[1:2])
}
-
输出
[1 2 3] [1 2] [2]array := [3]string{"1", "2", "3"} 索引: 0 1 2 +-----+ +-----+ +-----+ | "1" | | "2" | | "3" | +-----+ +-----+ +-----+ aa[0:2] 的意思是: - 从索引 0 开始(包含) - 到索引 2 结束(不包含) - 所以取索引 0 和 1:["1", "2"] -
切片排序
| 函数 | 作用 | 输入 | 输出 |
|---|---|---|---|
sort.IntSlice |
类型转换 | []int |
sort.IntSlice(实现了排序接口) |
sort.Reverse |
反转比较逻辑 | sort.Interface |
新的 sort.Interface(降序) |
sort.Sort |
执行排序 | sort.Interface |
无(直接修改原数据) |
一句话: 先把数组变成可排序的类型,再反转比较逻辑,最后执行排序,实现降序排序!
package main
import (
"fmt"
"sort"
)
func main() {
array := []int{1, 4, 5, 3}
//默认升序
sort.Ints(array)
fmt.Println(array)
//降序
//sort.IntSlice(array) 将 array 转换为 sort.IntSlice 类型
//sort.Reverse(...) 创建一个反向排序的包装器
//sort.Sort(...) 执行排序
sort.Sort(sort.Reverse(sort.IntSlice(array)))
fmt.Println(array)
}
-
输出
[1 3 4 5] [5 4 3 1]
map
map创建一定要初始化
取值
package main
import (
"fmt"
)
func main() {
// 键的类型是 int,值的类型是 string
var userMap = map[int]string{
1: "aa",
2: "bb",
4: "",
}
//
value, ok := userMap[4]
value2, ok2 := userMap[3]
fmt.Println(userMap[1])
fmt.Println(value, ok)
fmt.Println(value2, ok2)
}
-
输出
aa true false部分 含义 类型 value取到的值(如果不存在,则是零值) map 的值类型 ok是否存在该键(true/false) bool
设置
package main
import (
"fmt"
)
func main() {
var userMap = map[int]string{
1: "aa",
2: "bb",
}
userMap[1] = "cc"
fmt.Println(userMap)
delete(userMap, 2)
fmt.Println(userMap)
}
- 输出
map[1:cc 2:bb]
map[1:cc]
判断语句
if
嵌套式
package main
import (
"fmt"
)
func main() {
var age int
fmt.Printf("请输入:\n")
fmt.Scan(&age)
if age <= 0 {
fmt.Println("未出生")
return
}
if age <= 18 {
fmt.Println("未成年")
return
}
if age >= 18 {
fmt.Println("成年了")
return
}
}
嵌套式
package main
import (
"fmt"
)
func main() {
var age int
fmt.Printf("请输入:\n")
fmt.Scan(&age)
if age <= 18 {
if age <= 0 {
fmt.Println("未出生")
return
}else{
fmt.Println("未成年")
return
}
}
if age >= 18 {
fmt.Println("成年了")
return
}
}
多条件
package main
import (
"fmt"
)
func main() {
var age int
fmt.Printf("请输入:\n")
fmt.Scan(&age)
if age <= 18 && age > 0 {
fmt.Println("未成年")
return
}
if age > 18 {
fmt.Println("成年了")
return
}
}
### switch
package main
import (
"fmt"
)
func main() {
var age int
fmt.Printf("请输入:\n")
fmt.Scan(&age)
switch age {
case age <= 0:
fmt.Println("未出生")
case age < 18:
fmt.Println("未成年")
fallthrough //满足条件后继续往下走
case age > 18:
fmt.Println("成年了")
case age < 35:
fmt.Println("青年")
default:
return
}
}
package main
import (
"fmt"
)
func main() {
var week int
fmt.Printf("请输入:\n")
fmt.Scan(&week)
switch week {
case 1:
fmt.Println("周1")
case 2:
fmt.Println("周2")
case 3:
fmt.Println("周3")
case 4:
fmt.Println("周4")
case 5:
fmt.Println("周5")
case 6, 7:
fmt.Println("周末")
default:
fmt.Println("error")
}
}
for
经典写法
package main
import (
"fmt"
)
func main() {
var sum int
for i := 1; i <= 100; i++ {
fmt.Println(i)
sum += i
}
fmt.Println(sum)
}
死循环
package main
import (
"fmt"
"time"
)
func main() {
for {
fmt.Println(time.Now)
time.Sleep(2 * time.Second) // time.Second time 包定义的常量,表示 1 秒
}
}
package main
import (
"fmt"
)
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%d\t", i, j, i*j)
}
fmt.Println("")
}
}
while模式
package main
import (
"fmt"
)
func main() {
var sum int
i := 1
for i <= 100 {
sum += i
i++
}
fmt.Println(sum)
}
do while
package main
import (
"fmt"
)
func main() {
var sum int
i := 1
for {
sum += i
i++
if i == 101 {
break
}
}
fmt.Println(sum)
}
遍历list、map
package main
import (
"fmt"
)
func main() {
var list = []string{"1", "2"}
for i := 0; i < len(list); i++ {
fmt.Println(i, list)
}
for index, item := range list {
fmt.Println(index, item)
}
}
package main
import (
"fmt"
)
func main() {
var userMap = map[string]string{
"id1": "1",
"id2": "2",
}
for key, value := range userMap {
fmt.Println(key, value)
}
}
break & continue
| 关键字 | 作用 | 效果 |
|---|---|---|
break |
跳出整个循环 | 终止循环,不再执行 |
continue |
跳过本次循环 | 继续下一次循环 |
continue
package main
import (
"fmt"
)
func main() {
for i := 0; i <= 10; i++ {
if i == 5 {
continue
}
fmt.Println(i)
}
}
- 输出
0
1
2
3
4
6
7
8
9
10
break
package main
import (
"fmt"
)
func main() {
for i := 0; i <= 10; i++ {
if i == 5 {
break
}
fmt.Println(i)
}
}
- 输出
0 1 2 3 4
函数
简单函数
package main
import (
"fmt"
)
func say(id string, username string) {
fmt.Println(id, username)
}
func main() {
say("1", "admin")
}
多参数
不同数据类型的 range 返回值
| 数据类型 | for i := range x 的 i |
for i, v := range x 的 i 和 v |
|---|---|---|
| 数组/切片 | 索引 (int) | 索引, 元素值 |
| map | 键 (key type) | 键, 值 |
| 字符串 | 字节索引 (int) | 索引, Unicode 码点 (rune) |
| channel | 元素值 | 只能写一个变量 |
package main
import (
"fmt"
)
func say(num ...int) {
var sum int
for _, i := range num {
sum += i
}
fmt.Println(sum)
}
func main() {
say(1, 2, 3, 4)
}
返回值
package main
import "fmt"
//空返回
func say() {
return
}
//返回单个参数
func say2() bool {
return true
}
//返回多个参数
func say3() (string, bool) {
return "dd", true
}
//命名式函数返回
func say4() (ok1 string, ok2 bool) {
if 1 < 2 {
ok1 = "11"
return
}
return
}
func main() {
fmt.Println(say4())
}
匿名函数
package main
import "fmt"
var say1 = func(name string) {
fmt.Println(name)
return
}
func main() {
say1("111")
}
高阶函数
package main
import "fmt"
func main() {
fmt.Println("请输入操作")
fmt.Println("1、a")
fmt.Println("2、b")
var index int
fmt.Scan(&index)
switch index {
case 1:
login()
case 2:
regist()
}
}
func login() {
fmt.Println("登录")
}
func regist() {
fmt.Println("注册")
}
package main
import "fmt"
func main() {
fmt.Println("请输入操作")
fmt.Println("1、a")
fmt.Println("2、b")
var index int
fmt.Scan(&index)
var funMap = map[int]func(){
1: login,
2: regist,
}
// fun 是"值"(存在就是函数,不存在就是 nil)
// ok 是"是否存在"(true存在,false不存在)
// 这是go的设计,fun和ok都是获取值,只是如果值不存在的话,fun就变成nil,而ok变成了false
fun, ok := funMap[index]
if ok {
fun()
}
}
func login() {
fmt.Println("登录")
}
func regist() {
fmt.Println("注册")
}
闭包
设计一个函数,先传参表示延迟,再次传参就是求和
package main
import (
"fmt"
"time"
)
func awaitAdd(awaitSed int) func(...int) int {
return func(numberList ...int) (sum int) {
for _, i := range numberList {
sum += i
}
time.Sleep(time.Duration(awaitSed) * time.Second)
return
}
}
func main() {
add := awaitAdd(2)
// 这行代码做了两件事:
// 调用 awaitAdd(2) 函数
// 把返回的函数赋值给变量 add
t1 := time.Now()
sum := add(1, 2, 3)
t2 := time.Since(t1)
fmt.Println(sum, t2)
}
值传递和引用传递
package main
import (
"fmt"
)
// 先用 & 取地址,再用 %p 打印
func copy(name string) {
fmt.Printf("%p\n", &name) // 需要 &,因为 name 是值
name = "222222"
}
func set(name *string) { // *表示是引用传递
fmt.Printf("%p\n", name) // 不需要 &,因为 name 本身就是地址
*name = "33333"
fmt.Println(*name)
}
func main() {
name := "111111"
fmt.Printf("%p\n", &name) //打印原来的地址
copy(name) //打印函数中的name地址
set(&name)
}
结构体
定义
| 代码部分 | 职责 | 具体作用 | 类比 |
|---|---|---|---|
| type Stu struct { Name string } | 定义变量(数据模板) | • 定义结构体有哪些字段 • 指定字段的数据类型 • 创建数据的"蓝图" | 像表格的表头:规定要有"姓名"这一列 |
| func (s Stu) Study() { ... } | 绑定执行逻辑(行为方法) | • 定义具体的行为逻辑 • 通过接收者 (s Stu) 绑定到结构体 • 可以访问结构体的字段数据 |
像操作手册:规定如何执行"学习"这个动作 |
| func main() { s1 := Stu{Name: "aa"}; s1.Study() } | 初始化并执行(实际使用) | • 创建具体的结构体实例 • 给字段赋实际值 • 调用方法执行具体逻辑 | 像填写表格:在表头下填入"aa",然后按照手册操作 |
package main
import (
"fmt"
)
type Stu struct {
Name string
}
func (s Stu) Study() {
fmt.Printf("%s 在学习\n", s.Name)
}
func main() {
s1 := Stu{Name: "aa"}
s1.Study()
}
继承
package main
import (
"fmt"
)
type Class struct {
Name string
}
type Stu struct {
Class
Name string
}
func (s Stu) Study() {
fmt.Printf("%s 在学习\n", s.Name)
}
func (s Stu) info() {
fmt.Printf("学生属于:%s\n", s.Class.Name)
}
func main() {
c1 := Class{Name: "1班"}
s1 := Stu{Class: c1, Name: "aa"}
s1.Study()
s1.info()
}
结构体指针
package main
import (
"fmt"
)
type Class struct {
Name string
}
type Stu struct {
Class
Name string
}
func (s Stu) Study() {
fmt.Printf("%s 在学习\n", s.Name)
}
func (s Stu) info() {
fmt.Printf("学生属于:%s\n", s.Class.Name)
}
func (s *Stu) setName(name string) {
s.Name = name
fmt.Printf("%p 函数内\n", s)
}
func main() {
c1 := Class{Name: "1班"}
s1 := Stu{Class: c1, Name: "aa"}
fmt.Printf("%p 函数外\n", &s1)
s1.Study()
s1.info()
s1.setName("change")
s1.Study()
}
结构体Tag
| 部分 | 说明 |
|---|---|
json.Marshal() |
JSON包中的函数,用于序列化(将Go数据转成JSON) |
user |
要转换的Go数据(通常是结构体、map等) |
byteData |
返回的字节切片 []byte,内容是JSON字符串 |
_ |
忽略错误(下划线表示忽略该返回值) |
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"名字"`
Age int `json:"年龄,omitempty"` // omitempty抛弃空值
Password string `json:"-"` //"-" 代表忽略字段
}
func main() {
user := User{Name: "yl", Age: 10, Password: "000000"}
byteData, _ := json.Marshal(user)
fmt.Println(string(byteData))
}
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string
Age int
Password string
}
func main() {
user := User{Name: "yl", Age: 10, Password: "000000"}
byteData, _ := json.Marshal(user)
fmt.Println(string(byteData))
}
- 输出
{"名字":"yl","年龄":10}
自定义数据类型
package main
import (
"fmt"
)
type Code int
const (
SuccessCode Code = 200
ServiceErrCode Code = 201
NetworkErrCode Code = 202
)
func (c Code) getMsg() string {
switch c {
case SuccessCode:
return "成功"
case ServiceErrCode:
return "服务器正常"
case NetworkErrCode:
return "网络错误"
}
return ""
}
func webServer(name string) (code Code, msg string) {
if name == "1" {
return SuccessCode, SuccessCode.getMsg()
}
if name == "2" {
return ServiceErrCode, ServiceErrCode.getMsg()
}
if name == "3" {
return NetworkErrCode, NetworkErrCode.getMsg()
}
return 0, ""
}
func main() {
var name string
fmt.Printf("\n请输入")
fmt.Scan(&name)
code, msg := webServer(name)
fmt.Printf("代码:%d,msg:%s", code, msg)
}
package main
import (
"fmt"
)
type Code int
const (
SuccessCode Code = 200
ServiceErrCode Code = 201
NetworkErrCode Code = 202
)
func (c Code) getMsg() string {
switch c {
case SuccessCode:
return "成功"
case ServiceErrCode:
return "服务器正常"
case NetworkErrCode:
return "网络错误"
}
return ""
}
func (c Code) ok() (code Code, msg string) {
return c, c.getMsg()
}
func webServer(name string) (code Code, msg string) {
if name == "1" {
return SuccessCode.ok()
}
if name == "2" {
return ServiceErrCode.ok()
}
if name == "3" {
return NetworkErrCode.ok()
}
return 0, ""
}
func main() {
var name string
fmt.Printf("\n请输入")
fmt.Scan(&name)
code, msg := webServer(name)
fmt.Printf("代码:%d,msg:%s", code, msg)
}
类型别名
- 不能绑定方法
- 打印类型还是原始类型
- 和原始类型比较,类型别名不用转换
type myCode int //自定义类型
type youCode = int //类型别名
func (m myCode)Code() {
}
func (y youCode)Code() {
// 会报错,无法绑定方法
}
package main
import (
"fmt"
)
type myCode int //自定义类型
type youCode = int //类型别名
const Mycode myCode = 1
const YouCode youCode = 1
func main() {
fmt.Printf("%v,%T", Mycode, Mycode)
fmt.Printf("%v,%T", YouCode, YouCode)
}
- 输出
1,main.myCode1,int
接口
接口是一组仅包含方法名、参数、返回值的未具体实现的方法的集合
package main
import (
"fmt"
)
type singer interface {
Sing()
Jump()
}
// 鸡
type chicken struct {
Name string
}
func (c chicken) Sing() {
fmt.Println(c.Name + " is singing!")
}
func (c chicken) Jump() {
fmt.Println(c.Name + " is jumping!")
}
// 猫
type cat struct {
Name string
}
func (c cat) Sing() {
fmt.Println(c.Name + " is singing!")
}
func (c cat) Jump() {
fmt.Println(c.Name + " is jumping!")
}
// 同一个函数处理所有
func sing(c singer) {
c.Sing()
}
func jump(c singer) {
c.Jump()
}
func main() {
ch := chicken{Name: "chicken"}
ca := cat{Name: "cat"}
sing(ch)
sing(ca)
jump(ch)
jump(ca)
}
类型断言
func sing(c singer) {
info, ok := c.(chicken)
fmt.Println(info, ok)
c.Sing()
}
- 输出
{chicken} true
chicken is singing!
{} false
cat is singing!
chicken is jumping!
cat is jumping!
func sing(c singer) {
c.Sing()
switch server := c.(type) {
case chicken:
fmt.Println(server, "This is a chicken.")
case cat:
fmt.Println(server, "This is a cat.")
default:
fmt.Println(server, "Unknown singer.")
}
}
- 输出
chicken is singing!
{chicken} This is a chicken.
cat is singing!
{cat} This is a cat.
chicken is jumping!
cat is jumping!
空接口
- 空接口可以接受任何类型
- 任何类型都实现了空接口的定义
type EmptyInterface interface {}
func Print(val EmptyInterface) {
fmt.Println(val)
}
或
func Print(val interface{}) {
fmt.Println(val)
}
或
func Print(val any) {
fmt.Println(val)
}
- 输出
{chicken}
{cat}
协程
主线程结束,协程会跟着结束
package main
import (
"fmt"
"time"
)
func shopping(name string) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
}
func main() {
startTime := time.Now()
go shopping("u1")
go shopping("u2")
go shopping("u3")
fmt.Println("完成", time.Since(startTime))
}
- 输出
Starting: C:\Users\15051\go\bin\dlv.exe dap --listen=127.0.0.1:59975 from C:\Users\15051\Desktop\go\Put
DAP server listening at: 127.0.0.1:59975
Type 'dlv help' for list of commands.
完成 0s
Process 12056 has exited with status 0
Detaching
WaitGroup
package main
import (
"fmt"
"sync"
"time"
)
var wait sync.WaitGroup
func shopping(name string) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
wait.Done()
}
func main() {
startTime := time.Now()
wait.Add(3)
go shopping("u1")
go shopping("u2")
go shopping("u3")
wait.Wait()
fmt.Println("完成", time.Since(startTime))
}
- 输出
Starting: C:\Users\15051\go\bin\dlv.exe dap --listen=127.0.0.1:58718 from C:\Users\15051\Desktop\go\Put
DAP server listening at: 127.0.0.1:58718
Type 'dlv help' for list of commands.
Shopping start u3
Shopping start u1
Shopping start u2
Shopping end u1
Shopping end u3
Shopping end u2
完成 2.0013009s
Process 20140 has exited with status 0
Detaching
package main
import (
"fmt"
"sync"
"time"
)
func shopping(name string, wait *sync.WaitGroup) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
wait.Done()
}
func main() {
var wait sync.WaitGroup
startTime := time.Now()
wait.Add(3)
go shopping("u1", &wait)
go shopping("u2", &wait)
go shopping("u3", &wait)
wait.Wait()
fmt.Println("完成", time.Since(startTime))
}
Channel
package main
import (
"fmt"
"sync"
"time"
)
var Moneychannel = make(chan int) // 声明并初始化一个长度为0的信道
func shopping(name string, money int, wait *sync.WaitGroup) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
Moneychannel <- money
wait.Done()
}
func main() {
var wait sync.WaitGroup
startTime := time.Now()
wait.Add(3) // 设置等待的goroutine数量为3
go shopping("u1", 10, &wait)
go shopping("u2", 20, &wait)
go shopping("u3", 30, &wait)
go func() {
defer close(Moneychannel) // 是 Go 语言中的延迟调用语句,它的意思是:在函数返回之前执行 close(Moneychannel)。
wait.Wait() // 倒计时,等待计数器归零,归零就关闭信道
}() // 这个()是为了立即执行这个匿名函数
// for {
// money, ok := <-Moneychannel
// fmt.Println(money, ok)
// if !ok {
// break
// }
// }
var moneyList []int
for money := range Moneychannel {
fmt.Println(money)
moneyList = append(moneyList, money)
}
fmt.Println("完成", time.Since(startTime))
fmt.Println("完成", moneyList)
}package main
import (
"fmt"
"sync"
"time"
)
var Moneychannel = make(chan int) // 声明并初始化一个长度为0的信道
func shopping(name string, money int, wait *sync.WaitGroup) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
Moneychannel <- money
wait.Done()
}
func main() {
var wait sync.WaitGroup
startTime := time.Now()
wait.Add(3) // 设置等待的goroutine数量为3
go shopping("u1", 10, &wait)
go shopping("u2", 20, &wait)
go shopping("u3", 30, &wait)
go func() {
wait.Wait() // 倒计时,等待计数器归零,归零就关闭信道
close(Moneychannel)
}()
for {
money, ok := <-Moneychannel
fmt.Println(money, ok)
if !ok {
break
}
}
fmt.Println("完成", time.Since(startTime))
}
Select与协程超时处理
Select
package main
import (
"fmt"
"sync"
"time"
)
var Moneychannel1 = make(chan int) // 声明并初始化一个长度为0的信道
var Moneychannel2 = make(chan string) // 声明并初始化一个长度为0的信道
var Donechannel = make(chan string) // 声明一个用于关闭的信道
func shopping(name string, money int, wait *sync.WaitGroup) {
fmt.Println("Shopping start", name)
time.Sleep(2 * time.Second)
fmt.Println("Shopping end", name)
Moneychannel1 <- money
Moneychannel2 <- name
wait.Done()
}
func main() {
var wait sync.WaitGroup
startTime := time.Now()
wait.Add(3)
go shopping("u1", 10, &wait)
go shopping("u2", 20, &wait)
go shopping("u3", 30, &wait)
go func() {
defer close(Moneychannel1)
defer close(Moneychannel2)
defer close(Donechannel)
wait.Wait()
}()
var moneyList []int
var nameList []string
var event = func() {
for {
select {
case money1 := <-Moneychannel1:
moneyList = append(moneyList, money1)
case name := <-Moneychannel2:
nameList = append(nameList, name)
case <-Donechannel:
return
}
}
}
event()
fmt.Println("结束")
fmt.Println("完成", moneyList, nameList)
fmt.Println("完成", time.Since(startTime))
}
超时处理
package main
import (
"fmt"
"time"
)
var done = make(chan struct{})
func event() {
fmt.Println("开始")
time.Sleep(2 * time.Second)
fmt.Println("结束")
close(done)
}
func main() {
go event()
select {
case <-done:
fmt.Println("事件完成")
case <-time.After(1 * time.Second):
fmt.Println("事件超时")
return
//定时器
// 当代码执行到 select 时:
// time.After(3 * time.Second) 立即执行,创建定时器并开始倒计时
// 定时器在后台独立运行(由 Go 运行时管理)
// select 同时等待两个 channel:
// done channel(来自你的 event)
// 定时器 channel(3秒后会收到数据)
}
}
线程安全
不安全
package main
import (
"fmt"
"sync"
)
var sum int
var wait sync.WaitGroup
func add() {
for i := 0; i < 10000; i++ {
sum++
}
wait.Done()
}
func sub() {
for i := 0; i < 10000; i++ {
sum--
}
wait.Done()
}
func main() {
wait.Add(2)
go add()
go sub()
wait.Wait()
fmt.Println("最终结果:", sum)
}
安全
package main
import (
"fmt"
"sync"
)
var sum int
var wait sync.WaitGroup
var lock sync.Mutex
func add() {
lock.Lock()
for i := 0; i < 10000; i++ {
sum++
}
lock.Unlock()
wait.Done()
}
func sub() {
lock.Lock()
for i := 0; i < 10000; i++ {
sum--
}
lock.Unlock()
wait.Done()
}
func main() {
wait.Add(2)
go add()
go sub()
wait.Wait()
fmt.Println("最终结果:", sum)
}
sync.map
不安全
package main
import (
"fmt"
)
var maps = map[int]string{}
func main() {
go func() {
for {
maps[1] = "aaa"
}
}()
go func() {
for {
fmt.Println(maps[1])
}
}()
select {} // 快速等待
}
- 输出
fatal error: concurrent map read and map write
goroutine 8 [running]:
internal/runtime/maps.fatal({0x7ff703ab280e?, 0x1e37f5dea340?})
C:/Program Files/Go/src/runtime/panic.go:1181 +0x18
main.main.func2()
C:/Users/15051/Desktop/go/Put/main.go:17 +0x2c
created by main.main in goroutine 1
C:/Users/15051/Desktop/go/Put/main.go:15 +0x26
exit status 2
安全
package main
import (
"fmt"
"sync"
)
var maps = sync.Map{}
func main() {
var maps = sync.Map{}
go func() {
for {
maps.Store(1, "aaaaa")
}
}()
go func() {
for {
val, ok := maps.Load(1)
fmt.Println(val, ok)
}
}()
select {} // 快速等待
}
异常处理
向上抛
package main
import (
"errors"
"fmt"
)
func div(a, b int) (res int, err error) {
if b == 0 {
err = errors.New("除数不能为0")
return
}
res = a / b
return
}
func server() (res int, err error) {
res, err = div(10, 0)
if err != nil {
return
}
return
}
func main() {
res, err := server()
if err != nil {
fmt.Println("调用server失败,错误信息:", err)
return
}
fmt.Println("调用server成功,结果是:", res)
}
中断程序
package main
import (
"fmt"
"log"
"os"
)
func init() {
_, err := os.ReadFile("1111")
fmt.Println(err) // The system cannot find the file specified.
if err != nil {
// log.Fatalln("错误了")
// panic("错误了") //除了初始化,一般不用panic
}
}
func main() {
fmt.Println("main")
}
异常捕获
package main
import (
"fmt"
)
func read() {
var list []int = []int{1, 2}
fmt.Println(list[2])
}
func main() {
read()
}
- 输出
main.main.func2()
C:/Users/15051/Desktop/go/Put/main.go:17 +0x2c
created by main.main in goroutine 1
C:/Users/15051/Desktop/go/Put/main.go:15 +0x26
exit status 2
PS C:\Users\15051\Desktop\go\Put> go run .\main.go
panic: runtime error: index out of range [2] with length 2
goroutine 1 [running]:
main.read()
C:/Users/15051/Desktop/go/Put/main.go:9 +0x9
main.main()
C:/Users/15051/Desktop/go/Put/main.go:13 +0xf
exit status 2
PS C:\Users\15051\Desktop\go\Put>
正常
package main
import (
"fmt"
)
func read() {
defer func() {
err := recover()
fmt.Println("Recovered from panic:", err)
}()
var list []int = []int{1, 2}
fmt.Println(list[2])
}
func main() {
read()
fmt.Println("Program continues after recovery.")
}
- 输出
PS C:\Users\15051\Desktop\go\Put> go run .\main.go
Recovered from panic: runtime error: index out of range [2] with length 2
Program continues after recovery.
PS C:\Users\15051\Desktop\go\Put>
package main
import (
"fmt"
"runtime/debug"
)
func read() {
defer func() {
err := recover()
if err != nil {
fmt.Println("Panic recovered:", err, string(debug.Stack()))
}
}()
var list []int = []int{1, 2}
fmt.Println(list[2])
}
func main() {
read()
fmt.Println("Program continues after recovery.")
}
- 输出
PS C:\Users\15051\Desktop\go\Put> go run .\main.go
Panic recovered: runtime error: index out of range [2] with length 2 goroutine 1 [running]:
runtime/debug.Stack()
C:/Program Files/Go/src/runtime/debug/stack.go:26 +0x5e
main.read.func1()
C:/Users/15051/Desktop/go/Put/main.go:12 +0x38
panic({0x7ff7b2a82360?, 0x7c813322198?})
C:/Program Files/Go/src/runtime/panic.go:860 +0x13a
main.read()
C:/Users/15051/Desktop/go/Put/main.go:16 +0x30
main.main()
C:/Users/15051/Desktop/go/Put/main.go:20 +0x13
Program continues after recovery.
PS C:\Users\15051\Desktop\go\Put>
泛型
package main
import (
"fmt"
)
// 1. 泛型函数
func plus[T int | uint](n1, n2 T) T {
return n1 + n2
}
// 2. 泛型函数,多个类型参数
func plus1[T int, K string| uint](n1 T, n2 K) {
}
// 3. 泛型函数,类型参数约束
type Number interface{
int | uint | int8 | int16
}
func plus2[T Number](n1, n2 T) T {
return n1 + n2
}
func main() {
plus(1, 2)
var u1, u2 uint = 1, 2
fmt.Println(plus(u1, u2))
}
文件操作
读取
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开名为 "hi.txt" 的文件
// os.Open 返回两个值:
// file: 文件对象指针
// err: 错误信息(如果打开失败)
file, err := os.Open("hi.txt")
if err != nil {
// 如果打开文件出错(比如文件不存在),程序崩溃并打印错误
panic(err)
}
// defer 确保函数返回前关闭文件
// 即使后面代码出错,文件也会被正确关闭,避免资源泄露
defer file.Close()
// 循环读取文件内容,直到文件末尾
for {
// 创建长度为12字节的切片,用于存放读取的数据
// 每次最多读取12字节
var byteData = make([]byte, 12)
// 从文件读取数据到 byteData 切片
// n: 实际读取的字节数
// err: 错误信息
n, err := file.Read(byteData)
// 判断是否读到文件末尾
// io.EOF (End Of File) 表示文件读取完毕
if err == io.EOF {
break // 退出循环
}
// 打印读取的内容
// string(byteData): 将字节切片转为字符串
// n: 实际读取的字节数
fmt.Println(string(byteData), n)
}
// 循环结束后,defer 会触发 file.Close() 关闭文件
}
缓冲
按行读取
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 打开名为 "hi.txt" 的文件
// os.Open 返回两个值:
// file: 文件对象指针
// err: 错误信息(如果打开失败)
file, err := os.Open("hi.txt")
if err != nil {
// 如果打开文件出错(比如文件不存在),程序崩溃并打印错误
panic(err)
}
// defer 确保函数返回前关闭文件
// 即使后面代码出错,文件也会被正确关闭,避免资源泄露
defer file.Close()
buf := bufio.NewReader(file) // 创建一个新的 bufio.Reader 对象,用于读取文件内容
for {
line, _, err := buf.ReadLine() // 逐行读取文件内容
if err == io.EOF {
break
}
fmt.Println(string(line)) // 将读取到的行转换为字符串并打印
}
}
写入
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.OpenFile("ww.txt", os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
defer file.Close()
byteData, err := io.ReadAll(file)
if err != nil {
return
}
fmt.Println(string(byteData))
}
单元测试
在要测试的文件同路径,创建文件_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
res := Add(1, 2)
if res != 3 {
t.Errorf("测试失败")
return
}
t.Logf("测试通过")
}
终端执行 go test .\main_test.go .\main.go -v
子测试
package main
import "testing"
func TestAdd(t *testing.T) {
t.Run("add1", func(t *testing.T) {
if Add(1, 2) != 3 {
t.Error("1 + 2 should be 3")
}
t.Logf("测试成功")
})
t.Run("add2", func(t *testing.T) {
if Add(1, 2) != 0 {
t.Error("1 + 2 should be 3")
}
t.Logf("测试成功")
})
}
反射
要反射一个对象,必须先通过这两个入口:
对象 obj
|
+-------+--------+
| |
reflect.ValueOf(obj) reflect.TypeOf(obj)
| |
v v
值操作 类型操作
| |
|-- .Kind() |-- .Kind()
|-- .Int() |-- .Name()
|-- .String() |-- .NumField()
|-- .Field(i) |-- .Field(i)
|-- .SetInt() |-- .Method(i)
|-- .Interface() |-- .PkgPath()
+-- ... +-- ...
类型判断
package main
import (
"fmt"
"reflect"
)
func getType(obj any) {
t := reflect.TypeOf(obj)
switch t.Kind() {
case reflect.Int:
fmt.Println("int")
case reflect.String:
fmt.Println("string")
}
}
func main() {
getType(1)
getType("hello")
}
获取值
package main
import (
"fmt"
"reflect"
)
func getValue(obj any) {
t := reflect.ValueOf(obj)
switch t.Kind() {
case reflect.Int:
fmt.Println("int", t.Int())
case reflect.String:
fmt.Println("string", t.String())
}
}
func main() {
getValue(1)
getValue("hello")
}
修改值
package main
import (
"fmt"
"reflect"
)
func setValue(obj any, value any) {
v1 := reflect.ValueOf(obj)
v2 := reflect.ValueOf(value)
if v1.Elem().Kind() != v2.Kind() {
return
}
switch v1.Elem().Kind() {
case reflect.Int:
v1.Elem().SetInt(v2.Int())
case reflect.String:
v1.Elem().SetString(v2.String())
}
}
func main() {
var name = "ff"
var age = 18
setValue(&name, "tt")
setValue(&age, 20)
fmt.Println(name, age)
}
结构体的反射
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"name"`
Age int
IsMan bool
}
func Json(obj any) {
v := reflect.ValueOf(obj) //获取对象的反射值
t := reflect.TypeOf(obj) //获取对象的反射类型
for i := 0; i < v.NumField(); i++ { //遍历对象的字段
jsonTage := t.Field(i).Tag.Get("json") //获取字段的tag中的json标签
if jsonTage == "" {
jsonTage = t.Field(i).Name //如果没有json标签,就用字段的名称
}
fmt.Println(jsonTage) //获取字段的tag
fmt.Println(t.Field(i).Name, t.Field(i).Tag) //获取字段的名称
fmt.Println(v.Field(i)) //获取字段的值
}
// reflect.ValueOf(obj):获取整个对象的反射值
// v.Field(i):从对象的反射值中获取第i个字段的反射值
// v.FieldByName("Name"):根据名称获取字段
// t.Field(i).Name:获取字段的名称(需要 Type)
}
func main() {
s := Student{Name: "yl", Age: 18, IsMan: true}
Json(s)
}
修改结构体的值
| 场景 | 传入参数 | 反射代码 | 能否读取 | 能否修改 | 说明 |
|---|---|---|---|---|---|
| 只读操作 | 结构体 s |
v := ValueOf(s) t := TypeOf(s) |
✅ 可以 | ❌ 不能 | 最简单,最安全 |
| 只读操作 | 指针 &s |
v := ValueOf(&s).Elem() t := TypeOf(&s).Elem() |
✅ 可以 | ✅ 能(但不推荐) | 没必要,多此一举 |
| 修改操作 | 指针 &s |
v := ValueOf(&s).Elem() t := TypeOf(&s).Elem() |
✅ 可以 | ✅ 可以 | 必须这样写 |
| 修改操作 | 结构体 s |
v := ValueOf(s) |
✅ 可以 | ❌ 不能 | 编译通过,但修改会 panic |
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"名字"`
Age int
Class int
}
func getUserInfo(data any) {
v := reflect.ValueOf(data).Elem()
t := reflect.TypeOf(data).Elem()
fmt.Println(v, t)
for i := 0; i < v.NumField(); i++ {
v1 := v.Field(i)
if v1.Kind() == reflect.Int {
fmt.Println("是int")
}
if v1.Kind() != reflect.Int {
fmt.Println("不是int")
}
}
}
func main() {
data := Student{Name: "user1", Age: 10, Class: 1}
getUserInfo(&data)
}
调用结构体方法
| 对比 | TypeOf(x).Method(i) |
ValueOf(x).Method(i) |
|---|---|---|
| 返回类型 | reflect.Method |
reflect.Value |
| 返回内容 | 方法的描述信息 | 方法的可调用值 |
| 能否调用 | 能(通过 .Func.Call()) |
能(直接 .Call()) |
| 主要用途 | 获取方法名、方法签名 | 直接调用方法 |
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"名字"`
Age int
Class int
}
func (Student) Call(name string) {
fmt.Println("我被调用了", name)
}
func getUserInfo(data any) {
v := reflect.ValueOf(data).Elem()
t := reflect.TypeOf(data).Elem()
fmt.Println(v, t)
for i := 0; i < v.NumMethod(); i++ {
m := t.Method(i) // 获取第 i 个方法的信息
if m.Name != "Call" {
continue
}
method := v.Method(i) // 方法的可调用值
method.Call([]reflect.Value{
reflect.ValueOf("test"),
}) // 这里的 Call 是固定的!无参数也要传空切片 []reflect.Value{}
}
}
func main() {
data := Student{Name: "name", Age: 1, Class: 10}
getUserInfo(&data)
}
ORM例子
| 操作 | 拿到的是什么 | 类型 | 示例 |
|---|---|---|---|
v.Field(i) |
第 i 个字段的值 | reflect.Value |
"张三", 18 |
t.Field(i).Name |
第 i 个字段的键 | string |
"Name", "Age" |
t.Field(i).Type |
第 i 个字段的类型 | reflect.Type |
string, int |
t.Field(i).Tag |
第 i 个字段的标签 | reflect.StructTag |
json:"name" |
package main
import (
"errors"
"fmt"
"reflect"
"strings"
)
type Class struct {
Name string `orm:"name"`
Id int `orm:"id"`
}
// ...any
// 1. 可以传任意多个参数
// 2. 参数都是any类型
func Find(obj any, query ...any) (sql string, err error) {
// obj必须是结构体
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Struct {
err = errors.New("obj必须是一个结构体")
return
}
var where string
// 验证条件
if len(query) > 0 {
q := query[0] //第一个参数的类型必须是字符串
qs, ok := q.(string)
if !ok {
err = errors.New("第一个参数的类型必须是字符串")
return
}
// 算问号的个数
if strings.Count(qs, "?")+1 != len(query) {
err = errors.New("查询参数个数不匹配")
return
}
// 拼接
for _, a := range query[1:] {
switch s := a.(type) { // a.(type) 只能在 switch 语句中使用,用于获取接口变量的动态类型。
// 根据 a 的实际类型,做不同的处理,并把 a 的值转换成对应类型赋给 s
case string:
qs = strings.Replace(qs, "?", fmt.Sprintf("'%s'", s), 1) //fmt.Sprintf格式化字符串并返回
case int:
qs = strings.Replace(qs, "?", fmt.Sprintf("%d", s), 1) //fmt.Sprintf格式化字符串并返回
}
}
where = " where " + qs
}
// 拼接所有的有orm的字段
var columns []string
for i := 0; i < t.NumField(); i++ {
orm := t.Field(i).Tag.Get("orm")
if orm == "" {
continue
}
columns = append(columns, orm)
}
// 拼表名,小写结构体的名字s
name := strings.ToLower(t.Name()) + "s"
sql = fmt.Sprintf("select %s from %s %s;",
strings.Join(columns, ","),
name,
where,
)
return
}
func main() {
sql1, err := Find(Class{}, "id = ? and name = ?", 1, "三年一班")
fmt.Println(sql1, err)
// select name ,id from class where name = ? '三年一班'
}
- 输出
select name,id from classs where id = 1 and name = '三年一班'; <nil>
网络编程
TCP
服务端
package main
import (
"fmt"
"net"
"time"
)
func main() {
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:81")
listen, err := net.ListenTCP("tcp", addr)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf(" The Server Bind %s", addr.String())
for {
conn, err := listen.Accept()
if err != nil {
break
}
fmt.Println(conn.RemoteAddr())
conn.Write([]byte("hello world"))
time.Sleep(2 * time.Second)
conn.Close()
}
}
客户端
package main
import (
"fmt"
"io"
"net"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:81")
if err != nil {
fmt.Println(err)
return
}
for {
var byteData = make([]byte, 1024)
n, err := conn.Read(byteData)
if err == io.EOF {
break
}
fmt.Println(string(byteData[:n]))
}
}
HTTP
服务端
package main
import (
"fmt"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path, r.UserAgent())
w.Write([]byte("Hello World!"))
}
func main() {
http.HandleFunc("/", index)
fmt.Println("Listening on 127.0.0.1")
http.ListenAndServe("127.0.0.1:80", nil)
}
package main
import (
"fmt"
"net/http"
"os"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path, r.UserAgent())
byteData, err := os.ReadFile("Web/index.html")
if err != nil {
w.Write([]byte("文件不存在"))
return
}
w.Write(byteData)
}
func main() {
http.HandleFunc("/", index)
fmt.Println("Listening on 127.0.0.1")
http.ListenAndServe("127.0.0.1:80", nil)
}
客户端
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
res, err := http.Get("http://127.0.0.1:80")
if err != nil {
fmt.Println(err)
return
}
byteData, _ := io.ReadAll(res.Body)
fmt.Println(string(byteData))
}
部署
go build .\hello.go
go build -o main.exe .\hello.go
交叉编译
创建一个bat,执行
set CGO_ENABLED=0
set GOOS=linux
set GOARCH=amd64
go build -o main main.go
set GOOS=windows
其他
range
for range 是 Go 语言中的迭代循环,可以遍历数组、切片、字符串、map 和 channel
1. 基本语法
go
for 索引, 值 := range 集合 {
// 循环体
}
2. 在不同类型上的使用
遍历切片/数组
go
nums := []int{10, 20, 30}
// 方式1:同时获取索引和值
for i, num := range nums {
fmt.Printf("索引:%d, 值:%d\n", i, num)
}
// 输出:
// 索引:0, 值:10
// 索引:1, 值:20
// 索引:2, 值:30
// 方式2:只需要值,忽略索引
for _, num := range nums {
fmt.Println(num)
}
// 方式3:只需要索引
for i := range nums {
fmt.Println(i)
}
遍历 map
go
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// 遍历键值对
for name, age := range ages {
fmt.Printf("%s 年龄 %d\n", name, age)
}
// 只遍历键
for name := range ages {
fmt.Println(name)
}
遍历字符串(遍历 Unicode 字符)
go
str := "Hello 世界"
// 遍历每个字符(rune)
for i, ch := range str {
fmt.Printf("索引:%d, 字符:%c, Unicode:%U\n", i, ch, ch)
}
// 输出:
// 索引:0, 字符:H, Unicode:U+0048
// 索引:1, 字符:e, Unicode:U+0065
// ...
// 索引:6, 字符:世, Unicode:U+4E16
// 索引:9, 字符:界, Unicode:U+754C
遍历 channel(你代码中的用法)
go
ch := make(chan int, 3)
ch <- 10
ch <- 20
ch <- 30
close(ch) // 必须关闭,否则range会永远等待
// range会自动从channel接收数据,直到channel关闭
for num := range ch {
fmt.Println(num)
}
// 输出:
// 10
// 20
// 30
3. channel 的 for range 工作原理
当你在 channel 上使用 for range 时:
go
for money := range Moneychannel {
fmt.Println(money)
}
它等价于:
go
for {
money, ok := <-Moneychannel
if !ok { // 如果channel已关闭且缓冲区为空
break // 自动退出循环
}
fmt.Println(money)
}