🐹 学习 Go 语言基础知识。
Go 基础认识 关于 Go 🦜 编译型语言(性能高),Go 语言 => 编辑器 => 可执行文件 => 操作系统 + CPU 执行。解释型语言(跨平台好)。
下载 Go 下载 Go ,直接下一步安装,我这安装到了 D:\Soft\Go
目录,其中 D:\Soft\Go\bin\go.exe
是编译器,D:\Soft\Go\src
是源码目录。
如何关闭 GOMOD?
1 go env -w "GO111MODULE=off"
1 2 3 4 5 6 7 8 9 10 11 GOPATH C:\Users\dangp\go go version
第一个 Go 程序 1 2 3 4 5 6 7 8 package mainimport "fmt" func main () { fmt.Println("Hello World" ) }
1 2 3 4 5 6 go mod init ifer go build go build -o 包名 go run test.go
下载 GoLand https://www.jetbrains.com.cn/go/download/#section=windows
main.go
1 2 3 4 5 6 7 package mainimport "fmt" func main () { fmt.Println("Hello World" ) }
创建项目时,保证 GOROOT 的位置正确。如何运行 Go 文件?
变量和常量 如何定义变量
变量是对具体值的引用。
1. 先声明再赋值。
1 2 3 var name string name = "危险" fmt.Println(name)
1 2 3 var x int8 = 127 fmt.Print(x)
2. 声明和赋值一起。
1 2 3 4 var name string = "危险" var name = "危险"
3. 变量的默认值是类型的默认值。
1 2 3 var age int fmt.Println(age)
4. 可以一次性声明多个变量。
1 2 3 4 5 6 7 var ( name string address string ) name = "危险" address = "河南" fmt.Println(name, address)
或者
1 2 3 4 5 6 7 8 9 10 11 package mainimport "fmt" func main () { var ( name string = "IFER" address string = "河南" ) fmt.println (name, address) }
再或者
1 2 var a, b, c = 1 , 2 , 3 fmt.Println(a, b, c)
5. 短变量(推荐)。
1 2 3 4 name := "危险" age := 18 fmt.Println(name, age)
如何交换变量 1 2 3 4 5 a := 4 b := 7 a, b = b, a fmt.Println(a, b)
如何打印内存地址?
运算符
描述
实例
&
返回变量存储地址
例如 &a,将给出变量的实际地址
*
指针变量。
例如 *a,表示一个指针变量
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { name := "危险" fmt.Printf("name 的值是:%s,内存地址是:%p" , name, &name) }
变量的值拷贝 1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" func main () { var age1 = 18 var age2 = age1 age2 = 19 fmt.Println(age1, age2) }
什么是匿名变量 1 2 3 var _, b = 1 , 2 fmt.Println(b)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func test () (int , int ) { return 10 , 20 } func main () { a, _ := test() fmt.Println(a) }
如何定义常量 1 2 3 4 5 6 7 8 9 10 package mainfunc main () { const URL, API string = "wps.cn" , "https://www.google.com" }
iota(了解) 在 Go 语言中,iota 是一个特殊的常量生成器,用于在 const 声明中创建一组相关的常量。它在每一个新的 const 块中从 0 开始,每次遇到新的常量声明时,其值会自动递增 1。
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 package mainimport "fmt" func main () { const ( a = iota b = iota c = iota d = 0 e = iota f g h ) const ( i = iota j = 8 k = iota ) fmt.Println(a, b, c, d, e, f, g, h) fmt.Println(i, j, k) }
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { const ( A = iota B = 9 C D ) fmt.Println(A, B, C, D) }
关于全局和局部变量 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 package mainimport "fmt" var c int func main () { a := 1 b := 2 c = a + b fmt.Printf("c的地址是: %p" , &c) c := a + b fmt.Printf("c的地址是: %p" , &c) c = 8 fmt.Printf("c的地址是: %p" , &c) fmt.Println(c) }
首字母大写和小写 首字母大写的变量、方法、属性是公有的(能被外部访问),否则是私有的(不能被外部访问)。
基本数据类型 数字默认的类型是 int(int 类型的大小取决于所使用的平台),带 u 的是无符号,只能存正整数,后面的数字表示 2 进制的位数,uint8 还有一个别名 Byte,一个字节=8 个 bit 位。
1 2 3 4 var x1 byte = 'a' fmt.Printf("%c %d\n" , x1, x1) // a 97 var x2 uint8 = 97 fmt.Printf("%c %d\n" , x2, x2) // a 97
整型
类型
取值范围
int8
-128 到 127
uint8
0 到 255,2 的 8 次方是 256
int16
-32768 到 32767
uint16
0 到 65535
int32
-2147483648 到 2147483647
uint32
0 到 4294967295
int64
-9223372036854775808 到 9223372036854775807
uint64
0 到 18446744073709551615
uint
与平台相关,32 位操作系统上就是 uint32
,64 位操作系统上就是 uint64
int
与平台相关,32 位操作系统上就是 int32
,64 位操作系统上就是 int64
如何计算 1 2 uint32 => 0 ~ Math.pow(2, 32) - 1 int32 => -Math.pow(2, 31) ~ Math.pow(2, 31) - 1
越界报错 1 2 3 4 5 var x int x = 9223372036854775808 fmt.Print(x)
进制转换 1 2 3 4 5 6 7 var a int = 10 fmt.Printf("%d \n" , a) fmt.Printf("%b \n" , a) fmt.Printf("%o \n" , a) fmt.Printf("%x \n" , a)
查看类型 1 2 3 4 5 6 7 8 9 10 11 package mainimport ( "fmt" "reflect" ) func main () { var x = 1 fmt.Println(reflect.TypeOf(x)) }
特殊的类型 byte 和 rune 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { var num1 byte = 255 fmt.Printf("%T\n" , num1) var num2 rune = 1000000000 fmt.Printf("%T\n" , num2) }
浮点型 浮点型有两种,float32 和 float64,默认为 float64(可保留的小数点位数更多)
1 2 var f3 = 1.47 fmt.Println(f3, reflect.TypeOf(f3))
1 2 3 4 5 var x = 3e-2 var y = 4e2 fmt.Println(x, y)
布尔 1 2 var b1 = true fmt.Println(reflect.TypeOf(b1))
字符串 基本操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var str = "hello world" s1 := str[1 ] fmt.Println(s1) fmt.Println(string (s1)) s2 := str[2 :5 ] fmt.Println(s2) fmt.Println(reflect.TypeOf(s2)) s3 := str + " 123" fmt.Println(s3)
转义符 1 2 var str = "hello world \"🤠\"" fmt.Println(str)
多行字符串 通过 `` 包裹的能严格保留内部的格式。
1 2 3 4 5 var str = ` 1. a 2. b ` fmt.Println(str)
常用方法
方法
介绍
len(str)
求长度
strings.ToUpper
,strings.ToLower
生成一个新的全部大写的字符串,生成一个新的全部小写的字符串
strings.ReplaceAll(str, ".", "🦜")
生成一个新的原字符串被指定替换后的字符串
strings.Contains
判断是否包含
strings.HasPrefix,strings.HasSuffix
前缀/后缀判断
strings.Trim
去除字符串两端匹配的内容
strings.Index(),strings.LastIndex()
子串出现的位置
strings.Split
分割,将字符串按指定的内容分割成数组
strings.Join(a[]string, sep string)
join 操作,将数组按指定的内容拼接成字符串
类型转换 Go 语言不存在隐式类型转换,所有的类型转换都必须显式的声明:A = A(B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { a := 5.9 b := int (a) fmt.Printf("%T,%.1f\n" , a, a) fmt.Printf("%T,%d\n" , b, b) c := 1 d := float64 (c) fmt.Printf("%T,%d\n" , c, c) fmt.Printf("%T,%f\n" , d, d) }
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 30 31 32 33 34 var a int8 = 20 fmt.Println(int64 (a), reflect.TypeOf(int64 (a))) fmt.Println(float64 (a), reflect.TypeOf(float64 (a))) x := strconv.Itoa(98 ) fmt.Println(x, reflect.TypeOf(x)) y, _ := strconv.Atoi("98" ) fmt.Println(y, reflect.TypeOf(y)) x1, _ := strconv.ParseInt("1000" , 10 , 8 ) fmt.Println(x1) x2, _ := strconv.ParseInt("1000" , 10 , 64 ) fmt.Println(x2) x3, _ := strconv.ParseFloat("1.211" , 64 ) fmt.Println(x3, reflect.TypeOf(x3)) b1, _ := strconv.ParseBool("true" ) fmt.Println(b1, reflect.TypeOf(b1))
关于分支 fmt.Scan($num1)
,接受输入,注意参数要是一个地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" func main () { var num1 int var num2 int fmt.Printf("请输入密码 : \n" ) fmt.Scan(&num1) if num1 == 123456 { fmt.Println("请再次输入密码: " ) fmt.Scan(&num2) if num2 == 123456 { fmt.Println("登录成功!" ) } else { fmt.Println("登录失败!" ) } } else { fmt.Println("登录失败" ) } }
switch 比 if else 更简洁,效率更高!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var choice int fmt.Println("Choose a number:" ) fmt.Scanln(&choice) switch choice {case 1 : fmt.Println("牛" ) case 2 : fmt.Println("牛牛" ) case 3 : fmt.Println("牛牛牛" ) case 4 : fmt.Println("牛牛牛牛" ) default : fmt.Println("输入有误" ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "fmt" func main () { a := false switch a { case false : fmt.Println("1" ) fallthrough case true : fmt.Println("2" ) case false : fmt.Println("3" ) default : fmt.Println("6" ) } }
关于循环 求 1 ~ 100 的和。
1 2 3 4 5 6 7 8 9 10 var s = 0 for i := 1 ; i <= 100 ; i++ { s += i } fmt.Println(s)
无限循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 for true { var choice int fmt.Println("Choose a number:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Println("牛" ) case 2 : fmt.Println("牛牛" ) case 3 : fmt.Println("牛牛牛" ) case 4 : fmt.Println("牛牛牛牛" ) default : fmt.Println("输入有误" ) } }
打印一个十行十列的矩形。
1 2 3 4 5 6 for i := 0 ; i < 10 ; i++ { for j := 0 ; j < 10 ; j++ { fmt.Print(" * " ) } fmt.Print("\n" ) }
直接三角形。
1 2 3 4 5 6 7 8 9 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1 2 3 4 5 6 7 for i := 0 ; i < 10 ; i++ { for j := 0 ; j < i; j++ { fmt.Print(" * " ) } fmt.Println() }
输入输出 输出函数 1. fmt.Print(),不换行输出 2. fmt.Println(),换行输出 3. fmt.Printf(),格式化输出
1 2 3 4 5 6 7 8 9 10 11 12 13 name := "John Doe" age := 18 isMarried := false salary := 123.4 fmt.Printf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n" , name, age, isMarried, salary) fmt.Printf("姓名:%v 年龄:%v 婚否:%v 薪资:%v\n" , name, age, isMarried, salary) fmt.Printf("你好的类型是 %T,5 的类型是 %T" , "你好" , 5 ) fmt.Printf("%v %v" , "你好" , 5 ) fmt.Printf("%#v %#v" , "你好" , "" )
4. fmt.Sprintf(),不会输出到终端,而是返回加工后的字符串
1 2 3 4 5 info := fmt.Sprintf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n" , name, age, isMarried, salary) fmt.Println(info)
输入函数 1. fmt.Scan()
案例:输入求和。
1 2 3 4 5 6 7 var a, b int fmt.Print("请输入第一个数字:" ) fmt.Scan(&a) fmt.Print("请输入第一个数字:" ) fmt.Scan(&b) fmt.Println("合计:" , a+b)
2. fmt.Scanf()
,输入多个时,可以以指定的形式隔开
1 2 3 4 var a, b int fmt.Scan(&a, &b) fmt.Println(a + b)
3. fmt.Scanln()
了解 Scan 和 Scanln 的区别,如果是 Scan,先输入 1,再敲回车,会继续等待,可以再输入 2,而 Scanln 是遇到回车就结束啦。
1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" func main () { var a, b int fmt.Print("请输入两个整数,用空格分隔,然后按回车键: " ) fmt.Scanln(&a, &b) fmt.Printf("你输入的两个整数是: %d 和 %d\n" , a, b) }
Go 核心语法 指针 概念:所有数据必须放到内存中,将内存中数据的编号称为指针,指针也可以理解为地址。
符号
名称
作用
& 变量
取址符
返回变量所在的地址
* 指针变量
取值符
返回指针指地址存储的值
基本操作 如何获取数据的指针/地址?
1 2 3 var x = 1 fmt.Println (&x)
如何使用指针?
1. 通过 *类型
定义指针变量 。
2. 通过 &变量
取出地址(取址),为指针变量赋值。
3. 通过 *
访问指针变量中地址所指向的值,叫取值。
1 2 3 4 5 6 7 8 9 10 11 var x = 1 var p *int p = &x fmt.Println(p) fmt.Println(*p) fmt.Println(&p) *p = 888
上面演示的是 int 类型的指针变量,其实也可以有其他类型的指针变量,例如 string 类型的指针变量。
1 2 var s string = 'ifer' var p *string = &s
双指针,画图理解下面的操作。
1 2 3 4 5 6 var a = "张飞" var b = &avar c = &b**c = "吕布" fmt.Println(a)
指针赋值 Go 语言所有的都是值拷贝,只不过这个值是普通值还是地址值。
拷贝的是普通值!
1 2 3 4 var a = "张飞" var b = ab = "吕布" fmt.Println(a, b)
拷贝的是地址值!
1 2 3 4 var a = "张飞" var b = &a *b = "吕布" fmt.Println(a)
下面输出结果是什么?
1 2 3 4 5 6 7 var x = "张飞" var y = &x var z = *y z = "吕布" fmt.Println(x) fmt.Println(*y) fmt.Println(z)
数组【指针】 数组指针 ,首先应该是一个指针,指向了一个数组。
记住:* 是取值!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 arr := [4 ]int {1 , 2 , 3 , 4 } var p *[4 ]int p = &arr (*p)[0 ] = 4 p[1 ] = 7 fmt.Println(*p)
指针【数组】 指针数组 ,首先应该是一个数组,里面保存的是指针(有指针组成的数组)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 s1 := "张飞" s2 := "吕布" s3 := "赵云" arr := [3 ]*string {&s1, &s2, &s3} fmt.Println(arr) *arr[0 ] = "潘凤" fmt.Println(s1) s1 = "黄盖" fmt.Println(*arr[0 ])
指针函数 指针函数一般说的是:函数的返回值是指针!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package main import "fmt" func foo () *[4 ]string { arr := [4 ]string{"张飞" , "吕布" , "赵云" } return &arr } func main ( ) { p := foo () fmt.Println ((*p)[0 ]) (*p)[0 ] = "潘凤" fmt.Println (p[0 ]) }
指针作为函数参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func foo (p *int ) { *p = 2 } func main () { a := 1 foo(&a) fmt.Println("a:" , a) }
指针的题目 1 2 3 4 5 6 7 8 9 10 11 12 13 var x = 10 var y = xvar z = &xx = 20 fmt.Println(y) fmt.Println(*z) *z = 30 fmt.Println(x)
1 2 3 4 5 6 7 8 9 10 11 var x = 10 var y = &xvar z = *yx = 20 fmt.Println(x) fmt.Println(*y) fmt.Println(z)
1 2 3 4 5 6 7 var a = 100 var b = &avar c = &b**c = 200 fmt.Println(a)
new 函数 🤔 mark
new 的返回值是一个指针 ,用来分配内存,make 用来初始化 slice、map 和 channel。
基本数据类型 声明后都有默认零值,例如 int、string、bool 等,例如:
1 2 var x int fmt.Println(x)
指针类型是引用类型 ,如果声明后并没有默认值,直接操作是会报错的,如下:
1 2 3 4 var p *int fmt.Println(p) *p = 100
如何处理呢?答案:new,它的返回值是指向 new 的参数类型的指针。
1 2 3 4 5 var p *int = new (int )*p = 100 fmt.Println(p, *p)
数组 特点:数组是一组相同类型 的数据的有序集合;定义的时候需要指定长度;数组一旦定义了长度不可改变。
声明数组 语法:
举例:
1 2 3 var names [5 ]string fmt.Println(names, reflect.TypeOf(names))
注意下面的 x 和 y 的数据类型是不同的。
1 2 var x [3 ]string var y [5 ]string
初始化数组 先声明再赋值 1 2 3 4 var names [5 ]string names[0 ] = "张飞" names[1 ] = "吕布" fmt.Println(names)
声明并赋值 1 2 var names = [5 ]string {"张飞" , "吕布" }fmt.Println(names)
使用 :=
快速初始化 1 2 arr := [5 ]string {"吕布" , "张飞" } fmt.Println(arr2)
不限长度的数组 Go 的编译器会自动根据数组的长度来给 ...
赋值,自动推导长度。
1 2 var names = [...]string {"张飞" , "吕布" }fmt.Println(names)
通过索引设置数组 如何给数组的某几个特定的 index 位置赋值,其他没有操作到的索引对应到值是此类型的默认值。
1 2 var names = [...]string {0 : "张飞" , 5 : "吕布" }fmt.Println(names)
1 2 3 var names [10 ]string names = [10 ]string {3 : "张飞" , 7 : "吕布" } fmt.Println(names)
访问和修改数组元素 基于索引访问和修改数组 1 2 3 4 5 6 var names = [...]string {"张飞" , "吕布" , "赵云" , "典韦" }fmt.Println(names[0 ]) names[0 ] = "黄盖" fmt.Println(names[0 ])
切片取值 🤔 mark
字符串切出来的还是字符串,数组切出来的结果是切片。
1 2 3 4 var names = [...]string {"张飞" , "吕布" , "赵云" , "典韦" }fmt.Println(names[0 :2 ]) fmt.Println(names[0 :]) fmt.Println(names[:2 ])
遍历数组 1 2 3 4 5 6 7 8 9 10 var names = [...]string {"张飞" , "吕布" , "赵云" , "典韦" }for i := 0 ; i < len (names); i++ { fmt.Println(names[i]) } for _, value := range names { fmt.Println(value) }
for range 循环(数组、切片、映射)时候的 value 是值的副本,对这个副本的修改不会影响原数据。
1 2 3 4 5 6 7 var names = [...]string {"张飞" , "吕布" , "赵云" , "典韦" }for _, value := range names { value = "潘凤" fmt.Println(value) } fmt.Println(names)
数组是值类型特点 🤔 mark
体现:赋值和函数传参。
1 2 3 4 5 6 arr1 := [4 ]int {5 , 2 , 4 , 7 } arr2 := arr1 arr2[0 ] = 8 fmt.Println(arr1)
数组的冒泡排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { arr := [...]int {1 , 3 , 4 , 2 , 0 } for i := 0 ; i < len (arr)-1 ; i++ { for j := 0 ; j < len (arr)-i-1 ; j++ { if arr[j] < arr[j+1 ] { arr[j], arr[j+1 ] = arr[j+1 ], arr[j] } } } fmt.Println(arr) }
二维数组的定义和遍历 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 package mainimport "fmt" func main () { arr := [3 ][4 ]int { {0 , 1 , 2 , 3 }, {4 , 5 , 6 , 7 }, {8 , 9 , 10 , 11 }, } for i := 0 ; i < len (arr); i++ { for j := 0 ; j < len (arr[i]); j++ { fmt.Println(arr[i][j]) } } for _, v := range arr { for i := range v { fmt.Println(v[i]) } } }
切片 🤔 mark
切片是对现有数组的引用,他是一个动态的长度,所以,定义切片的时候不需要指定长度。切片本身不存储任何数据 ,都是底层的数组来存储的,所以修改了切片也就是修改了这个数组中的数据。
基本概念 从概念上面来说 slice 像一个结构体,这个结构体包含了三个元素:
1. 指针:「指向数组」 的开始位置。
2. 长度:即 slice 的长度。
3. 容量:也就是 slice 开始位置到数组的最后位置的长度。
声明切片的两种方式 声明并初始化切片 1 2 3 4 5 6 7 8 9 arr := [4 ]string {"张飞" , "吕布" , "赵云" } s := []string {"张飞" , "吕布" , "赵云" } s[0 ] = "潘凤" fmt.Printf("%T, %T\n" , arr, s) fmt.Println(s[0 ])
通过 make 函数 只声明的切片,后续不能直接赋值,需要通过 make 进行初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 arr := [4 ]string {"张飞" , "吕布" , "赵云" } fmt.Println(arr) var s1 []string fmt.Println(s1) if s1 == nil { fmt.Println("切片是空的" ) } s1[0 ] = "潘凤"
如何通过 make 初始化切片?
语法:make([]type, length, capacity)
1 2 3 4 5 6 7 8 9 var s []string s = make ([]string , 5 , 10 ) s[0 ] = "潘凤" fmt.Println(s)
🤔 mark
一个注意点:当切片 s 的长度为 0 时,不能直接通过索引 s[0] 来赋值,但是可以通过 append 来追加数据。
1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" func main () { s := make ([]string , 0 , 10 ) s = append (s, "张飞" ) fmt.Println(s) }
从数组或切片上取得数据 思考数组和切片的关系?
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 var arr = [3 ]string {"张飞" , "吕布" , "赵云" }var s1 = arr[1 :3 ]fmt.Println(s1, reflect.TypeOf(s1)) var s2 = arr[:]fmt.Println(s2, reflect.TypeOf(s2)) var s3 = s2[0 :2 ]s3[0 ] = "潘凤" fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(arr) fmt.Printf("原数组:%p\n" , &arr) fmt.Printf("%p\n" , s1) fmt.Printf("%p\n" , s2) fmt.Printf("%p\n" , s3)
切片原理 🤔 mark
1 2 3 4 5 6 7 8 9 10 11 12 var names = [5 ]string {"张飞" , "吕布" , "赵云" , "许褚" , "潘凤" }fmt.Println(&names[0 ], &names[1 ], &names[2 ], &names[3 ], &names[4 ]) s1 := names[0 :3 ] s2 := names[2 :5 ] s3 := names[0 :2 ] fmt.Println(&s1[0 ], &s2[0 ], &s3[0 ]) fmt.Println(&s1[0 ], &s1[1 ], &s1[2 ]) fmt.Printf("%p, %p, %p" , &s1, &s2, &s3)
切片和原数组是关联的吗?什么时候会失去关联 🤔 mark
在 Go 中,切片(slice)和原数组是关联的,这种关联的本质是:切片通过内部的指针指向原数组的一部分数据,因此切片和原数组共享同一块底层内存。
当切片发生扩容时,会创建一个新的底层数组,此时切片与原数组的关联会被打破(切片的指针会指向新数组)。扩容的触发条件是:当切片的长度(len)即将超过其容量(cap)时,对切片执行 append 操作会触发扩容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var arr = [3 ]string {"张飞" , "吕布" , "赵云" }var s1 = arr[1 :3 ] s1[0 ] = "潘凤" fmt.Println(arr) s1 = append (s1, "关羽" ) s1[0 ] = "颜良" fmt.Println(arr) fmt.Println(s1)
遍历切片的两种方式 1 2 3 4 5 6 7 8 9 10 11 s := []string {"张飞" , "吕布" , "赵云" } for i := 0 ; i < len (s); i++ { fmt.Println(s[i]) } for i, v := range s { fmt.Println(s[i], v) }
通过 append 追加数据 基本使用 1 2 3 4 5 s1 := make ([]string , 0 , 3 ) s1 = append (s1, "张飞" , "吕布" ) fmt.Println(s1, len (s1), cap (s1))
1 2 3 4 5 6 7 8 xxx := make ([]string , 3 , 5 ) xxx[0 ] = "a" xxx[1 ] = "b" xxx[2 ] = "c" zzz := append (xxx, "d" ) fmt.Println(zzz)
添加会自动扩容 容量只有 3 个,那能放超过 3 个的吗?可以,通过 append 操作切片,当容量不够时,会翻倍扩容。
1 2 3 s1 := make ([]string , 0 , 3 ) s1 = append (s1, "张飞" , "吕布" , "赵云" , "典韦" ) fmt.Println(s1, len (s1), cap (s1))
合并两个切片 1 2 3 4 5 s1 := []string {"张飞" , "吕布" } s2 := []string {"赵云" , "典韦" } s1 = append (s1, s2...) fmt.Println(s1)
扩容分析(重) 🤔 mark
向切片中添加数据的时候,如果没有超过容量,直接添加,如果超过了这个容量,就会自动扩容,cap 成倍的增加,拷贝,切片一旦扩容(超出了原来的容量)
,就是重新指向一个新的底层数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 s1 := []string {"张飞" , "吕布" , "赵云" } fmt.Printf("len:%d,cap:%d\n" , len (s1), cap (s1)) fmt.Printf("%p\n" , s1) s1 = append (s1, "典韦" , "潘凤" ) fmt.Printf("len:%d,cap:%d\n" , len (s1), cap (s1)) fmt.Printf("%p\n" , s1) s1 = append (s1, "黄忠" , "张辽" ) fmt.Printf("len:%d,cap:%d\n" , len (s1), cap (s1)) fmt.Printf("%p\n" , s1) s1 = append (s1, "马超" , "许褚" ) fmt.Printf("len:%d,cap:%d\n" , len (s1), cap (s1)) fmt.Printf("%p\n" , s1)
修改扩容后的数据会影响原数组吗 1 2 3 4 5 6 7 8 9 10 11 12 s1 := []string {"张飞" , "吕布" , "赵云" } fmt.Printf("len:%d,cap:%d\n" , len (s1), cap (s1)) fmt.Printf("%p\n" , s1) s2 := append (s1, "典韦" , "潘凤" ) fmt.Printf("len:%d,cap:%d\n" , len (s2), cap (s2)) fmt.Printf("%p\n" , s2) s2[0 ] = "许褚" fmt.Println(s1[0 ])
一个注意点
🤔 mark
1 2 fmt.Printf("%p\n" , s2) fmt.Printf("%p\n" , &s2)
1 2 3 4 5 6 7 8 9 10 11 12 slice1 := make ([]int , 5 , 10 ) slice2 := append (slice1, 1 ) fmt.Println(slice2) slice3 := slice1[:6 ] fmt.Println(slice3) slice4 := append (slice1, 3 ) fmt.Println(slice4) fmt.Println(slice1)
测试题目 1 2 3 4 5 6 7 8 9 10 l := make ([]int , 5 , 10 ) v1 := append (l, 1 ) fmt.Println(v1) fmt.Printf("%p\n" , &v1) v2 := append (l, 2 ) fmt.Println(v2) fmt.Printf("%p\n" , &v2) fmt.Println(v1)
开头添加 1 2 3 4 var s1 = []string {"张飞" , "吕布" }s2 := append ([]string {"典韦" }, s1...) fmt.Println(s2)
任意位置插入 1 2 3 var a []int a = append (a[:i], append ([]int {x}, a[i:]...)...) a = append (a[:i], append ([]int {x, y, z}, a[i:]...)...)
举例如下:
1 2 3 4 5 6 var s1 = []string {"张飞" , "吕布" }s2 := append (s1[:1 ], append ([]string {"黄忠" }, s1[1 :]...)...) fmt.Println(s2)
如何删除 方法 1 删除切片 a 中索引为 i 的元素,公式:
1 a = append (a[:index], a[index+1 :]...)
1 2 3 4 var s1 = []string {"张飞" , "吕布" , "赵云" , "许褚" }s1 = append (s1[:2 ], s1[3 :]...) fmt.Println(s1)
方法 2 1 2 3 4 5 6 7 8 9 10 var s1 = []string {"张飞" , "吕布" , "赵云" , "许褚" }var s2 []string var target = "赵云" for _, v := range s1 { if v != target { s2 = append (s2, v) } } fmt.Println(s2)
方法 3 1 2 3 4 5 6 7 8 9 10 var s1 = []string {"张飞" , "吕布" , "赵云" , "许褚" }var target = "赵云" for i, v := range s1 { if v == target { s1 = append (s1[:i], s1[i+1 :]...) } } fmt.Println(s1)
学生管理版本 1(切片里面装字符串) 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 var students []string for { fmt.Println(` 1. 查看学生 2. 添加学生 3. 删除学生 4. 修改学生 5. 退出学生 ` ) var choice int fmt.Println("Enter your choice: " ) fmt.Scan(&choice) switch choice { case 1 : fmt.Println("===查看学生===" ) for i, v := range students { fmt.Println(i+1 , ":" , v) } case 2 : fmt.Println("===添加学生===" ) var addStudent string fmt.Println("请输入要添加的学生姓名:" ) fmt.Scan(&addStudent) students = append (students, addStudent) fmt.Println("添加成功!" ) case 3 : fmt.Println("===删除学生===" ) var delStudent string fmt.Println("请输入要删除的学生姓名:" ) fmt.Scan(&delStudent) for i, v := range students { if v == delStudent { students = append (students[:i], students[i+1 :]...) } } fmt.Println("删除成功!" ) case 4 : fmt.Println("===修改学生===" ) fmt.Println("请输入需要修改的名字: " ) var name string fmt.Scan(&name) fmt.Println("请输入新名字: " ) var newName string fmt.Scan(&newName) for i, v := range students { if v == name { students[i] = newName fmt.Println("修改成功" ) } } fmt.Println("修改失败" ) case 5 : fmt.Println("===退出学生===" ) os.Exit(200 ) default : fmt.Println("===非法输入===" ) } }
切片是引用类型的特点 1 2 3 4 5 6 7 8 9 10 11 12 13 s1 := []string {"张飞" , "吕布" , "赵云" , "许褚" } s2 := s1 s1[0 ] = "潘凤" fmt.Println(s2) fmt.Printf("%p, %p\n" , s1, s2) fmt.Println(&s1[0 ], &s2[0 ]) fmt.Printf("%p, %p\n" , &s1, &s2)
切片排序 整型
1 2 3 4 5 6 a := []int {5 , 1 , 7 , 9 } sort.Ints(a) fmt.Println(a)
字符串
1 2 3 b := []string {"melon" , "banana" , "strawberry" , "apple" } sort.Strings(b) fmt.Println(b)
浮点数
1 2 3 c := []float64 {2.2 , 1.5 , 3.12 , 9 , 8.5 } sort.Float64s(c) fmt.Println(c)
通过 sort.IntSlice
将普通切片转换为可排序的类型,sort.Reverse
反转排序逻辑,sort.Sort
执行排序操作,最终实现了对整数切片的降序排序。
1 2 3 4 5 6 7 a := []int {5 , 1 , 7 , 9 } sort.Sort(sort.Reverse(sort.IntSlice(a))) fmt.Println(a)
如果是一个数组,需要先转成切片再排序。
深拷贝和浅拷贝 值类型的数据,默认都是深拷贝,例如 array、int、float、string、bool、struct 等。
引用类型的数据,默认都是浅拷贝,例如 slice、map、chan,拷贝的是地址,会导致多个变量指向同一块内存。
如何实现切片的深拷贝?
for 循环
1 2 3 4 5 6 7 s1 := []string {"张飞" , "吕布" , "赵云" , "许褚" } s2 := make ([]string , 0 ) for i := 0 ; i < len (s1); i++ { s2 = append (s2, s1[i]) } s2[0 ] = "潘凤" fmt.Println(s1[0 ])
append
1 2 3 4 5 s1 := []string {"张飞" , "吕布" , "赵云" , "许褚" } s2 := make ([]string , 0 ) s2 = append (s2, s1...) s2[0 ] = "潘凤" fmt.Println(s1[0 ])
copy
1 2 3 4 5 6 7 8 9 10 11 var s1 = []string {"张飞" , "吕布" , "赵云" , "许褚" }var s2 = make ([]string , len (s1))copy (s2, s1)fmt.Println(s2) s3 := []int {4 , 5 } s4 := []int {6 , 7 , 8 , 9 } copy (s4, s3)fmt.Println(s4)
make 函数 针对的是指针、切片、映射、管道。
1 2 3 4 5 6 7 8 9 var x = make ([]int , 3 )x[0 ] = 1 fmt.Println(x)
课后练习 1. 描述执行过程
1 2 3 4 var s []string s[0 ] = "张飞" fmt.Println(s)
1 2 3 4 5 6 7 s := make ([]string , 3 ) s = append (s, "张飞" , "吕布" , "赵云" ) fmt.Println(s)
🤔 mark
关键点:s 会复制原数组重新扩容,s 本身的地址没有改变(内容变了)。
2. 描述执行过程
1 2 3 4 5 6 7 p1 := 1 p2 := &p1 *p2++ fmt.Println(p1) fmt.Println(*p2)
3. 输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 a := [3 ]int {0 , 1 , 2 } s := a[1 :2 ] s[0 ] = 11 s = append (s, 12 ) s = append (s, 13 ) fmt.Println(s) s[0 ] = 21 fmt.Println(a) fmt.Println(s)
如何理解 s[0] = 11
,a 数组对应位置变成了 11,s 也变成了 {11}
如何理解 s = append(s, 12)
如何理解 s = append(s, 13)
,注意扩容 2 倍指的是原 s 容量的 2 倍。
扩容之后,Go 语言会重新分配一个新的底层数组,把原来的数据复制过去,然后在新数组里添加新元素。“原来的数据” 就是在扩容操作之前切片 s
中已经存在的元素,即 [11, 12]
。
如何理解 s[0] = 21
1 2 3 4 5 6 7 8 9 10 11 12 a := [3 ]int {0 , 1 , 2 } s := a[1 :2 ] s[0 ] = 11 s = append (s, 12 ) s = append (s, 13 ) fmt.Println(s) s[0 ] = 21 fmt.Println(a) fmt.Println(s) fullSlice := s[:cap (s)] fmt.Println(fullSlice)
4. 切片反转
1 2 3 4 5 6 7 s := []int {1 , 2 , 3 , 4 , 5 } for i, j := 0 , len (s)-1 ; i < j; i, j = i+1 , j-1 { s[i], s[j] = s[j], s[i] } fmt.Println(s)
1 2 i, j := 1 , 2 fmt.Print(i, j)
5. 观察结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var a = [5 ]int {1 , 2 , 3 , 4 , 5 }var r [5 ]int for i, v := range &a { if i == 0 { a[1 ] = 12 a[2 ] = 13 } r[i] = v } fmt.Println("r = " , r) fmt.Println("a = " , a)
5. new 和 make 的区别
make 初始化引用开辟空间
new 初始化指针开辟空间
6. 观察结果
1 2 3 4 5 6 7 8 9 arr := [4 ]int {10 , 20 , 30 , 40 } s1 := arr[0 :2 ] s2 := s1 s3 := append (append (append (s1, 1 ), 2 ), 3 ) s1[0 ] = 1000 fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(arr)
学生管理版本 2(切片里面装切片) 二维切片。
1 2 3 4 5 6 var s = [][]string { []string {"张飞" , "18" , "深圳" }, []string {"赵云" , "19" , "河南" }, } fmt.Println(s[1 ][0 ])
学生管理练习。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package mainimport "fmt" func main () { var stus [][]string fmt.Println(` 1. 查看学生 2. 添加学生 3. 删除学生 4. 修改学生 5. 退出系统 ` ) for { fmt.Print("请输入你的选择:" ) var choice int fmt.Scanln(&choice) switch choice { case 1 : fmt.Println("=====查看学生=====" ) for i, v := range stus { fmt.Printf("%d. 姓名:%s 年龄:%s 性别:%s\n" , i+1 , v[0 ], v[1 ], v[2 ]) } case 2 : fmt.Println("=====添加学生=====" ) fmt.Print("请输入学生姓名:" ) var name string fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) var age string fmt.Scanln(&age) fmt.Print("请输入学生性别:" ) var gender string fmt.Scanln(&gender) stus = append (stus, []string {name, age, gender}) fmt.Println("添加成功" ) case 3 : fmt.Println("=====删除学生=====" ) fmt.Print("请输入学生姓名:" ) var name string fmt.Scanln(&name) for i, v := range stus { if v[0 ] == name { stus = append (stus[:i], stus[i+1 :]...) fmt.Println("删除成功" ) break } } case 4 : fmt.Println("=====修改学生=====" ) fmt.Print("请输入要修改的学生姓名:" ) var name string fmt.Scanln(&name) for i, v := range stus { if v[0 ] == name { fmt.Print("请输入学生新名:" ) var name string fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) var age string fmt.Scanln(&age) fmt.Print("请输入学生性别:" ) var gender string fmt.Scanln(&gender) stus[i] = []string {name, age, gender} fmt.Println("修改成功" ) break } } case 5 : fmt.Println("=====退出系统=====" ) return default : fmt.Println("输入错误,请重新输入" ) } } }
切片里面放多类型 1 2 3 stu := []interface {}{"吕布" , 18 , "河南" } fmt.Println(stu)
Map 映射 基本概念 Map 是一种无序的键值对的集合,所以我们可以像迭代数组和切片那样迭代它,Map 也是引用类型,语法如下:
声明的两种方式 方式一:make
1 2 3 4 5 6 7 8 9 10 var map1 map [string ]string if map1 == nil { fmt.Println("map 没有初始化默认是 nil" ) } map1 = make (map [string ]string ) map1["address" ] = "IFER" fmt.Println(map1)
方式二:声明和初始化可以一起
1 2 3 var map2 = map [string ]int {"Go" : 100 , "Java" : 10 , "C" : 60 }fmt.Println(map2) fmt.Printf("%T\n" , map2)
操作 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 25 26 27 28 29 var map1 map [int ]string map1 = make (map [int ]string ) map1[100 ] = "JavaScript" map1[200 ] = "Java" fmt.Println(map1) fmt.Println(map1[200 ]) value, ok := map1[1 ] if ok { fmt.Println("map key 存在,value 是:" , value) } else { fmt.Println("map key 不存在!" ) } map1[100 ] = "xxx" delete (map1, 100 )fmt.Println(len (map1))
遍历 Map map 是无序的,每次打印出来的值可能都不一样,它不能通过 index 获取,只能通过 key 来获取。
map 的长度是不固定的,是引用类型。
len 可以用于 map 查看 map 中数据的数量,但是 cap 无法使用。
map 的 key 可以是布尔类型、整数、浮点数、字符串。
1 2 3 4 var map1 = map [string ]int {"Go" : 50 , "Java" : 89 , "C" : 87 , "Python" : 90 }for k, v := range map1 { fmt.Println(k, v) }
将 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 user1 := make (map [string ]string ) user1["name" ] = "张飞" user1["age" ] = "19" user2 := make (map [string ]string ) user2["name" ] = "吕布" user2["age" ] = "32" user3 := map [string ]string {"name" : "赵云" , "age" : "88" } userDatas := make ([]map [string ]string , 0 , 3 ) userDatas = append (userDatas, user1) userDatas = append (userDatas, user2) userDatas = append (userDatas, user3) for _, user := range userDatas { fmt.Println(user) }
学生管理版本 3(切片里面装 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package mainimport "fmt" func main () { var stus []map [string ]string fmt.Println(` 1. 查看学生 2. 添加学生 3. 删除学生 4. 修改学生 5. 退出系统 ` ) for { fmt.Print("请输入你的选择:" ) var choice int fmt.Scanln(&choice) switch choice { case 1 : fmt.Println("=====查看学生=====" ) for i, v := range stus { fmt.Printf("%d. 姓名:%s 年龄:%s 性别:%s\n" , i+1 , v["name" ], v["age" ], v["gender" ]) } case 2 : fmt.Println("=====添加学生=====" ) fmt.Print("请输入学生姓名:" ) var name string fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) var age string fmt.Scanln(&age) fmt.Print("请输入学生性别:" ) var gender string fmt.Scanln(&gender) stus = append (stus, map [string ]string {"name" : name, "age" : age, "gender" : gender}) fmt.Println("添加成功" ) case 3 : fmt.Println("=====删除学生=====" ) fmt.Print("请输入学生姓名:" ) var name string fmt.Scanln(&name) for i, v := range stus { if v["name" ] == name { stus = append (stus[:i], stus[i+1 :]...) fmt.Println("删除成功" ) break } } case 4 : fmt.Println("=====修改学生=====" ) fmt.Print("请输入要修改的学生姓名:" ) var name string fmt.Scanln(&name) for i, v := range stus { if v["name" ] == name { fmt.Print("请输入学生新名:" ) var name string fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) var age string fmt.Scanln(&age) fmt.Print("请输入学生性别:" ) var gender string fmt.Scanln(&gender) stus[i] = map [string ]string {"name" : name, "age" : age, "gender" : gender} fmt.Println("修改成功" ) break } } case 5 : fmt.Println("=====退出系统=====" ) return default : fmt.Println("输入错误,请重新输入" ) } } }
Map 嵌套 Map 1 2 3 4 5 6 7 var stu1 = map [string ]string {"name" : "张飞" , "age" : "20" , "address" : "河南" }var stu2 = map [string ]string {"name" : "潘凤" , "age" : "18" , "address" : "深圳" }var stus = map [int ]map [string ]string {1000 : stu1, 1001 : stu2}fmt.Println(stus[1001 ]["address" ])
Map 嵌套切片 1 2 3 4 5 6 7 8 var stu1 = map [string ]interface {}{"name" : "张飞" , "age" : "20" , "address" : "河南" , "hobby" : []string {"吃饭" , "睡觉" , "打豆豆" }}var stus = map [int ]map [string ]interface {}{1000 : stu1}fmt.Println(stus[1000 ]["hobby" ].([]string )[1 ])
函数 Go 代码中至少要有一个 main 入口函数。
声明和调用 1 2 3 4 func 函数名(形式参数 形参类型) (返回值类型) { 函数体 return 返回值 }
求和函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainfunc calc (num int ) int { var r = 0 for i := 1 ; i <= num; i++ { r += i } return r } func main () { r := calc(100 ) println (r) }
可变的参数 求任意多个实参的和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainfunc main () { r := getSum(1 , 2 ) println (r) } func getSum (nums ...int ) int { sum := 0 for i := 0 ; i < len (nums); i++ { sum += nums[i] } return sum }
函数返回值 函数没有指定返回值,那么函数调用的结果不能被接收,否则会报错。
1 2 3 4 5 6 7 8 9 10 11 package mainimport "fmt" func foo () {} func main () { r := foo() fmt.Println(r) }
举例,返回参数中大的那一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainfunc main () { r := getMax(1 , 2 ) println (r) } func getMax (num1 int , num2 int ) int { var result int if num2 > num1 { result = num2 } else { result = num1 } return result }
函数可以有多个返回值。
1 2 3 4 5 6 7 8 9 10 11 package mainfunc main () { a, b := swip("张飞" , "吕布" ) println (a, b) } func swip (x, y string ) (string , string ) { return y, x }
可以给返回值命名。
1 2 3 4 5 func foo () (ret int ) { ret = 10 return }
1 2 3 4 5 func calc (x, y int ) (sum, sub int ) { sum = x + y sub = x - y return }
给返回值命名为一个变量后,可以在函数体内直接使用这个变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { zc, area := calc(2 , 4 ) fmt.Println(zc, area) } func calc (len , wid float64 ) (area float64 , zc float64 ) { area = len * wid zc = (len + wid) * 2 return zc, area }
注意和下面的区分。
1 2 3 4 5 6 func calc (len , wid float64 ) (float64 , float64 ) { area := len * wid zc := (len + wid) * 2 return zc, area }
函数作用域(函数内变量的作用域) 函数外面的称为全局变量。
写在 if、for、函数,或者函数的形参,都是局部变量。局部变量不能在全局调用。
1 2 3 4 5 6 7 8 9 10 11 package mainimport "fmt" func main () { if temp := 1 ; true { println (temp) } fmt.Println(temp) }
函数的传参 普通值传递的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func foo (x int ) { x = 100 } func main () { var x = 10 foo(x) fmt.Println(x) }
传递指针的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func foo (p *int ) { *p = 100 } func main () { var a = 10 var p *int = &a foo(p) fmt.Println(a) }
传递切片的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func foo (s []string ) { fmt.Printf("foo 函数中接收的 s 的地址是:%p\n" , &s) s[0 ] = "马超" } func main () { var s = []string {"张飞" , "吕布" , "赵云" } fmt.Printf("main 函数中的 s 的地址:%p\n" , &s) foo(s) fmt.Println(s) }
匿名的函数 可以将匿名函数给变量。
1 2 3 4 5 6 7 var foo = func () { fmt.Println("将军" ) } fmt.Println(reflect.TypeOf(foo)) foo()
可以直接调用匿名函数。
1 2 3 4 5 6 7 8 9 10 (func () { fmt.Println("潘凤" ) })() var foo = (func (x, y int ) int { return x + y })(1 , 2 ) fmt.Println(foo)
函数内部不能声明普通函数,可以声明匿名函数。
1 2 3 4 5 6 package mainfunc main () { (func () {})() }
高阶函数 函数的参数是一个函数。
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 package mainimport "fmt" func main () { r2 := oper(1 , 2 , add) fmt.Println(r2) r3 := oper(1 , 2 , sub) fmt.Println(r3) } func oper (a, b int , fun func (int , int ) int ) int { r := fun(a, b) return r } func add (a, b int ) int { return a + b } func sub (a, b int ) int { return a - b }
递归函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { r := getSum(5 ) fmt.Println(r) } func getSum (n int ) int { if n == 1 { return 1 } return getSum(n-1 ) + n }
关于闭包 基本操作 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 package mainimport "fmt" func increment () func () int { i := 0 fun := func () int { i++ return i } return fun } func main () { foo := increment() foo() foo() r1 := foo() fmt.Println(r1) bar := increment() bar() bar() r2 := bar() fmt.Println(r2) }
形参也是局部变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "fmt" func increment (i int ) func () int { fun := func () int { i++ return i } return fun } func main () { foo := increment(10 ) foo() foo() r1 := foo() fmt.Println(r1) }
计算函数运行时间 通过高阶函数
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 30 31 package mainimport ( "fmt" "time" ) func foo () { fmt.Println("foo 功能开始" ) time.Sleep(3 * time.Second) fmt.Println("foo 功能结束" ) } func bar () { fmt.Println("bar 功能开始" ) time.Sleep(2 * time.Second) fmt.Println("bar 功能结束" ) } func timer (f func () ) { start := time.Now().Unix() f() end := time.Now().Unix() fmt.Println("cost timer:" , end-start) } func main () { timer(foo) timer(bar) }
违背了原来的调用方式
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 30 31 32 33 34 35 36 package mainimport ( "fmt" "time" ) func foo () { fmt.Println("foo 功能开始" ) time.Sleep(3 * time.Second) fmt.Println("foo 功能结束" ) } func bar () { fmt.Println("bar 功能开始" ) time.Sleep(2 * time.Second) fmt.Println("bar 功能结束" ) } func getTimer (f func () ) func () { return func () { start := time.Now().Unix() f() end := time.Now().Unix() fmt.Println("cost timer:" , end-start) } } func main () { foo := getTimer(foo) foo() bar := getTimer(bar) bar() }
关于 defer 作用:处理一些善后的问题,比如错误,文件、网络流关闭等。
基本使用 1 2 3 4 5 6 7 8 9 10 package mainfunc main () { 文件.Open() defer 文件.Close() }
一个 defer
1 2 3 4 5 6 7 8 9 package mainimport "fmt" func main () { fmt.Println("1" ) defer fmt.Println("2" ) fmt.Println("3" ) }
执行顺序 如果有多个 defer,先进后出(栈)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { defer f("1" ) fmt.Println("2" ) defer f("3" ) fmt.Println("4" ) } func f (s string ) { fmt.Println(s) }
拷贝机制 下面的执行结果是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { foo := func () { fmt.Println("foo1" ) } defer foo() foo = func () { fmt.Println("foo2" ) } }
继续观察传递参数的表现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { n := 10 fmt.Println("main start n=" , n) defer f(n) n++ fmt.Println("main end n=" , n) } func f (n int ) { fmt.Println("f 函数中 n=" , n) }
继续观察输出结果。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { x := 10 defer func (a int ) { fmt.Println(a) }(x) x++ }
继续观察
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { x := 10 defer func () { fmt.Println(x) }() x++ }
执行时机 1 2 3 4 5 6 func foo () { return 100 }
常见考题 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 package mainimport "fmt" func f1 () int { i := 5 defer func () { i++ }() return i } func f2 () *int { i := 5 defer func () { i++ fmt.Printf(":::%p\n" , &i) }() fmt.Printf(":::%p\n" , &i) return &i } func f3 () (result int ) { defer func () { result++ }() return 5 } func f4 () (result int ) { defer func () { result++ }() return result } func f5 () (r int ) { t := 5 defer func () { t = t + 1 }() return t } func f6 () (r int ) { fmt.Println(&r) defer func (r int ) { r = r + 1 fmt.Println(&r) }(r) return 5 } func f7 () (r int ) { defer func (x int ) { r = x + 1 }(r) return 5 } func main () { println (f1()) println (*f2()) println (f3()) println (f4()) println (f5()) println (f6()) println (f7()) }
关于文件操作 字节与字符 一个字节等于八个二进制位,例如 uint8 表示无符号整型,占 8 位,也就是 2 的 8 次方,byte 和 unit8 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。
byte 类型
1 2 3 4 5 var b byte b = 'A' fmt.Println(reflect.TypeOf(b)) fmt.Println(b) fmt.Println(string (b))
byte 等价于 uint8(无符号的 0 ~ 255)
1 2 3 4 var b uint8 b = 65 fmt.Println(b) fmt.Println(string (b))
rune 类型
1 2 3 4 5 var b rune b = '金' fmt.Println(b) fmt.Println(reflect.TypeOf(b)) fmt.Println(string (b))
rune 类型等价于 int32
1 2 3 4 5 var b int32 b = 37329 fmt.Println(b) fmt.Println(reflect.TypeOf(b)) fmt.Println(string (b))
文件的读取 os.Open() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "os" ) func main () { file1, err := os.Open("C:\\Users\\dangp\\Desktop\\gotest\\stus.json" ) if err != nil { fmt.Println(err) } fmt.Println(file1) }
os.OpenFile() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "os" ) func main () { file1, err := os.OpenFile("C:\\Users\\dangp\\Desktop\\gotest\\stus.json" , os.O_RDONLY|os.O_WRONLY, os.ModePerm) if err != nil { fmt.Println(err) } fmt.Println(file1) }
file.Read() 读取文件内容,file.Read()
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 file, _ := os.Open("D:\\Gogogo\\xxx.txt" ) defer file.Close()bs := make ([]byte , 3 , 1024 ) n, err := file.Read(bs) fmt.Println(string (bs)) fmt.Println(n, err) n, err = file.Read(bs) fmt.Println(string (bs)) fmt.Println(n, err) n, err = file.Read(bs) fmt.Println(string (bs)) fmt.Println(n) fmt.Println(err)
为什么多读出了一个汉字?读取到最后的时候它不会自动清空切片里之前的数据,可以使用下面的办法进行输出。
1 fmt.Println(string (bs[:n]))
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 30 31 32 package mainimport ( "fmt" "io" "os" ) func main () { file1, err := os.Open("C:\\Users\\dangp\\Desktop\\gotest\\xxx.txt" ) if err != nil { fmt.Println("打开文件失败:" , err) return } defer file1.Close() bs := make ([]byte , 3 ) for { n, err := file1.Read(bs) if err == io.EOF { break } if err != nil { fmt.Println("读取文件失败:" , err) break } fmt.Printf("读取了 %d 个字节: %s\n" , n, string (bs[:n])) } }
bufio.NewReader(file1).ReadLine() 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package mainimport ( "bufio" "fmt" "io" "os" ) func main () { file1, err := os.Open("C:\\Users\\dangp\\Desktop\\gotest\\xxx.txt" ) if err != nil { fmt.Println("打开文件失败:" , err) return } defer file1.Close() reader := bufio.NewReader(file1) for { line, isPrefix, err := reader.ReadLine() if err == io.EOF { break } if err != nil { fmt.Println("读取文件失败:" , err) break } if isPrefix { fmt.Printf("读取长行的一部分: %s\n" , string (line)) for isPrefix { line, isPrefix, err = reader.ReadLine() if err != nil { break } fmt.Printf("长行继续: %s\n" , string (line)) } } else { fmt.Printf("读取完整行: %s\n" , string (line)) } } }
bufio.NewReader(file1).ReadString(‘\n’) 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 package mainimport ( "bufio" "fmt" "io" "os" ) func main () { file1, err := os.Open("C:\\Users\\dangp\\Desktop\\gotest\\xxx.txt" ) if err != nil { fmt.Println("打开文件失败:" , err) return } defer file1.Close() reader := bufio.NewReader(file1) for { line, err := reader.ReadString('\n' ) fmt.Print(line) if err == io.EOF { break } } }
bufio.NewScanner(file) 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 package mainimport ( "bufio" "fmt" "os" ) func main () { file1, err := os.Open("C:\\Users\\dangp\\Desktop\\gotest\\xxx.txt" ) if err != nil { fmt.Println("打开文件失败:" , err) return } defer file1.Close() scanner := bufio.NewScanner(file1) for scanner.Scan() { line := scanner.Text() fmt.Println(line) } if err := scanner.Err(); err != nil { fmt.Println("读取文件时发生错误:" , err) } }
文件的写入
file.Write 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" "os" ) func main () { fileName := "xxx.txt" file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_RDONLY|os.O_APPEND, os.ModePerm) if err != nil { fmt.Println(err) } defer file.Close() bs := []byte {65 , 66 , 67 , 68 , 69 } n, err := file.Write(bs) if err != nil { fmt.Println(err) } fmt.Println(n) }
file.WriteString 1 2 3 4 5 6 7 fileName := "xxx.txt" file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_RDONLY|os.O_APPEND, os.ModePerm) if err != nil { fmt.Println(err) } defer file.Close()file.WriteString("哈哈哈哈哈哈哈" )
序列化(Map 类型的切片) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport ( "encoding/json" "fmt" "os" ) func main () { var stu1 = map [string ]string {"name" : "张飞" , "age" : "18" , "address" : "北京" } var stu2 = map [string ]string {"name" : "吕布" , "age" : "28" , "address" : "河南" } var stus = []map [string ]string {stu1, stu2} res, _ := json.Marshal(stus) fmt.Println(string (res)) os.WriteFile("stus.json" , res, 0666 ) }
反序列化
1 2 3 4 5 res, _ := os.ReadFile("stus.json" ) var data []map [string ]string json.Unmarshal(res, &data) fmt.Println(data)
文件信息 1 2 3 4 5 6 7 8 9 fileInfo, err := os.Stat("D:\\Gogogo\\stus.json" ) if err != nil { return } fmt.Println(fileInfo.Name()) fmt.Println(fileInfo.IsDir()) fmt.Println(fileInfo.ModTime()) fmt.Println(fileInfo.Size()) fmt.Println(fileInfo.Mode())
还有一种表示权限的标识:八进制,例如 chomd 7 7 7
。
目录的创建和删除 存在我就打开,不存在就创建这个文件夹。
1 2 3 4 5 6 7 err := os.Mkdir("D:\\Gogogo\\ifer" , os.ModePerm) if err != nil { fmt.Println(err) } fmt.Println("文件夹创建完毕" )
创建多层文件夹。
1 2 3 4 5 err2 := os.MkdirAll("D:\\Gogogo\\ifer\\a\\b\\c" , os.ModePerm) if err2 != nil { fmt.Println(err2) } fmt.Println("层级文件夹创建完毕" )
删除空文件夹,通过 remove 方法只能删除单个/一层空文件夹。
1 2 3 4 5 err3 := os.Remove("D:\\Gogogo\\ifer\\a\\b\\c" ) if err3 != nil { fmt.Println(err3) } fmt.Println("file delete success!!" )
如果存在多层文件,removeAll,相对来说比较危险,删除这个目录下的所有东西,强制删除。
1 2 3 4 5 err4 := os.RemoveAll("D:\\Gogogo\\ifer\\a" ) if err4 != nil { fmt.Println(err4) } fmt.Println("file delete success!!" )
创建和删除文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 file1, err := os.Create("a.go" ) if err != nil { fmt.Println(err) return } fmt.Println(file1) err = file1.Close() if err != nil { fmt.Println(err) return } err = os.Remove("a.go" ) if err != nil { fmt.Println(err) return } fmt.Println("文件删除成功" )
拷贝文件 io.Copy
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 source := "D:\\Gogogo\\a.txt" dest := "D:\\Gogogo\\b.txt" sourceFile, err := os.Open(source) if err != nil { fmt.Printf("打开源文件失败: %v\n" , err) return } defer sourceFile.Close()destFile, err := os.Create(dest) if err != nil { fmt.Printf("创建目标文件失败: %v\n" , err) return } defer destFile.Close()_, err = io.Copy(destFile, sourceFile) if err != nil { fmt.Printf("文件拷贝失败: %v\n" , err) return } fmt.Println("文件拷贝成功!" )
重要的结构体 先定义结构体 通过 type 和 struct 关键字声明结构体,格式如下:
1 2 3 4 type 类型名 struct { 字段1 字段1 的类型 字段2 字段2 的类型 }
举例:
1 2 3 4 5 6 type Student struct { sid int name, address string age int8 course []string }
实例化结构体
[!NOTE]
引用类型:切片(Slice)、映射(Map)、通道(Channel)、函数 …
结构体是值类型(基本数据类型、数组),结构体的地址与存储的第一个值的地址是相同的,而后面每一个成员变量的地址是连续的。
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 package mainimport ( "fmt" ) type Student struct { sid int name, address string age int8 course []string } func initStu (s Student) Student { s.age = 19 return s } func main () { stu := Student{ sid: 1 , name: "张三" , address: "北京" , age: 18 , course: []string {"数学" , "语文" , "英语" }, } initStu(stu) fmt.Println(stu.age) }
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 package mainimport ( "fmt" ) type Student struct { sid int name, address string age int8 course []string } func initStu (s *Student) { s.age = 19 } func main () { stu := Student{ sid: 1 , name: "张三" , address: "北京" , age: 18 , course: []string {"数学" , "语文" , "英语" }, } initStu(&stu) fmt.Println(stu.age) }
引用类型:切片(Slice)、映射(Map)、通道(Channel)、指针(Pointer)、函数(Function)、接口(Interface)。
先声明后赋值 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 mainimport "fmt" type Student struct { sid int name, address string age int8 course []string } func main () { var s1 Student fmt.Println(s1) s1.sid = 1 s1.name = "IFER" s1.address = "河南" s1.age = 18 s1.course = []string {"语文" , "数学" } fmt.Println(s1) fmt.Printf("%p\n" , &s1) fmt.Printf("%p\n" , &s1.sid) }
声明赋值一起 1 2 3 4 5 s1 := Student{sid: 1 , name: "IFER" , address: "河南" , age: 18 , course: []string {"语文" , "数学" }} fmt.Println(s1)
通过 new 操作符 new 操作符的返回值是结构体指针。
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 package mainimport ( "fmt" "reflect" ) type Student struct { sid int name, address string age int8 course []string } func main () { var s1 = new (Student) fmt.Println(reflect.TypeOf(s1)) (*s1).sid = 1 s1.name = "IFER" s1.address = "河南" s1.age = 18 s1.course = []string {"语文" , "数学" } fmt.Println(*s1) }
函数传值之结构体 结构体是值类型的体现。
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 package mainimport "fmt" type Student struct { sid int name, address string age int8 course []string } func main () { stu1 := Student{1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }} fmt.Printf("%T,%p\n" , stu1, &stu1) stu2 := stu1 fmt.Println(stu2) fmt.Printf("%T,%p\n" , stu2, &stu2) stu2.name = "ELSER" fmt.Println(stu1.name) }
函数参数传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "reflect" ) type Student struct { sid int name, address string age int8 course []string } func ageInit (s Student) { s.age = 0 } func main () { var s1 = Student{sid: 1 , name: "IFER" , address: "河南" , age: 18 , course: []string {"语文" , "数学" }} ageInit(s1) fmt.Println(s1.age) }
如何得到结构体的指针?
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 mainimport ( "fmt" "reflect" ) type Student struct { sid int name, address string age int8 course []string } func ageInit (s Student) { s.age = 0 } func main () { var s = &Student{sid: 1 , name: "IFER" , address: "河南" , age: 18 , course: []string {"语文" , "数学" }} fmt.Println(reflect.TypeOf(s)) fmt.Println((*s).name) fmt.Println(s.name) ageInit(*s) fmt.Println((*s).age) }
函数传参,如何传递结构体的指针?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "fmt" ) type Student struct { sid int name, address string age int8 course []string } func ageInit (s *Student) { s.age = 0 } func main () { var s = &Student{sid: 1 , name: "IFER" , address: "河南" , age: 18 , course: []string {"语文" , "数学" }} ageInit(s) fmt.Println(s.age) }
模拟构造函数 封装一个函数,返回结构体?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" ) type Student struct { sid int name, address string age int8 course []string } func NewStudent (sid int , name, address string , age int8 , course []string ) Student { return Student{sid, name, address, age, course} } func main () { var s = NewStudent(1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }) fmt.Println(s) }
封装一个函数,返回结构体指针?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "fmt" ) type Student struct { sid int name, address string age int8 course []string } func NewStudent (sid int , name, address string , age int8 , course []string ) *Student { return &Student{sid, name, address, age, course} } func main () { var s = NewStudent(1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }) fmt.Println(*s) }
结构体的方法 如何挂载方法 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 package mainimport "fmt" type Student struct { sid int name, address string age int8 course []string } func NewStudent (sid int , name, address string , age int8 , course []string ) Student { return Student{sid, name, address, age, course} } func (s Student) ageInit() { s.age = 0 } func main () { var s1 = NewStudent(1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }) s1.ageInit() fmt.Println(s1.age) }
接收结构体指针 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 mainimport "fmt" type Student struct { sid int name, address string age int8 course []string } func NewStudent (sid int , name, address string , age int8 , course []string ) *Student { return &Student{sid, name, address, age, course} } func (s *Student) ageInit() { s.age = 0 } func main () { var s1 = NewStudent(1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }) s1.ageInit() fmt.Println(s1.age) }
自动转换为指针 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 mainimport "fmt" type Student struct { sid int name, address string age int8 course []string } func NewStudent (sid int , name, address string , age int8 , course []string ) Student { return Student{sid, name, address, age, course} } func (s *Student) ageInit() { s.age = 0 } func main () { var s1 = NewStudent(1 , "IFER" , "河南" , 18 , []string {"语文" , "数学" }) s1.ageInit() fmt.Println(s1.age) }
关于匿名字段 结构体允许成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" type Student struct { string int } func main () { s := Student{ "IFER" , 18 , } fmt.Printf("%#v\n" , s) fmt.Println(s.string , s.int ) }
结构体本身也可以作为匿名字段使用。
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 30 package mainimport "fmt" type Address struct { country string province string city string } type Student struct { name string age int Address } func main () { s := Student{ "IFER" , 18 , Address{"中国" , "河南省" , "鹤壁" }, } fmt.Printf("%#v\n" , s) fmt.Println(s.name, s.age) fmt.Println(s.Address) fmt.Println(s.Address.country) fmt.Println(s.city) }
通过嵌套匿名结构体实现继承 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 30 31 32 33 34 35 36 37 38 package mainimport "fmt" type Animal struct { name string } func (a Animal) eat() { fmt.Printf("%s is eating!\n" , a.name) } func (a Animal) sleep() { fmt.Printf("%s is sleeping!\n" , a.name) } type Dog struct { Kind string Animal } func (d *Dog) bark() { fmt.Printf("%s is barking ~\n" , d.name) } func main () { d1 := Dog{ Kind: "金毛" , Animal: Animal{ name: "旺财" , }, } d1.eat() d1.bark() }
另一种指针的写法:
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 30 31 32 33 34 35 36 37 38 package mainimport "fmt" type Animal struct { name string } func (a *Animal) eat() { fmt.Printf("%s is eating!\n" , a.name) } func (a *Animal) sleep() { fmt.Printf("%s is sleeping!\n" , a.name) } type Dog struct { Kind string *Animal } func (d *Dog) bark() { fmt.Printf("%s is barking ~\n" , d.name) } func main () { d1 := &Dog{ Kind: "金毛" , Animal: &Animal{ name: "旺财" , }, } d1.eat() d1.bark() }
序列化(结构体) 序列化
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 mainimport ( "encoding/json" "os" ) type Addr struct { Province string City string } type User struct { Name string `json:"name"` Age int `json:"age"` Addr Addr `json:"addr"` } func main () { var user1 = User{Name: "张飞" , Age: 18 , Addr: Addr{Province: "河南" , City: "郑州" }} var user2 = User{Name: "吕布" , Age: 22 , Addr: Addr{Province: "广州" , City: "深圳" }} res, _ := json.Marshal([]User{user1, user2}) os.WriteFile("stus.json" , res, 0666 ) }
反序列化
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 30 package mainimport ( "encoding/json" "fmt" "os" ) type Addr struct { Province string City string } type User struct { Name string `json:"name"` Age int `json:"age"` Addr Addr `json:"addr"` } func main () { jsonBytes, _ := os.ReadFile("stus.json" ) var users []User json.Unmarshal(jsonBytes, &users) fmt.Println(users[0 ].Name) }
学生管理作业 相关提示 核心:for 循环 + 接收用户输入 + switch 语句。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package mainimport "fmt" type Student struct { ID int Name string Age int Gender int Email string Phone string } func main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : case 2 : case 3 : case 4 : case 5 : case 6 : case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
增加功能 核心:结构体类型的切片 + 通过 append 往切片里面添加结构体。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package mainimport "fmt" type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) fmt.Println(students) case 2 : case 3 : case 4 : case 5 : case 6 : case 7 : fmt.Println("退出系统" ) default : 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package mainimport "fmt" type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) fmt.Println(students) case 2 : for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } case 3 : var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) for _, student := range students { if student.ID == id { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) break } } case 4 : case 5 : case 6 : case 7 : fmt.Println("退出系统" ) default : 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 package mainimport "fmt" type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc addStudent () { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) } func getStudent () { for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func getStudentByID () { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) var index = -1 for i, student := range students { if student.ID == id { index = i break } } if index != -1 { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , students[index].ID, students[index].Name, students[index].Age, students[index].Email, students[index].Phone) return } fmt.Println("没有找到该学生!" ) } func main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : addStudent() case 2 : getStudent() case 3 : getStudentByID() case 4 : case 5 : case 6 : case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
修改学生 核心:封装 getIndexById 函数。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 package mainimport "fmt" type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc addStudent () { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) } func getStudent () { for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func getIndexById () (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range students { if student.ID == id { index = i break } } return } func getOneStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func updateStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : addStudent() case 2 : getStudent() case 3 : getOneStudent() case 4 : updateStudent() case 5 : case 6 : case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
删除学生 核心:students = append(students[:index], students[index+1:]...)
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 package mainimport "fmt" type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc addStudent () { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) } func getStudent () { for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func getIndexById () (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range students { if student.ID == id { index = i break } } return } func getOneStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func updateStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func delStudentByIndex () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } students = append (students[:index], students[index+1 :]...) fmt.Println("删除成功" ) } func main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : addStudent() case 2 : getStudent() case 3 : getOneStudent() case 4 : updateStudent() case 5 : delStudentByIndex() case 6 : case 7 : fmt.Println("退出系统" ) default : 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 package mainimport ( "encoding/json" "fmt" "os" ) type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc addStudent () { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) } func getStudent () { for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func getIndexById () (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range students { if student.ID == id { index = i break } } return } func getOneStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func updateStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func delStudentByIndex () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } students = append (students[:index], students[index+1 :]...) fmt.Println("删除成功" ) } func saveStudent () { data, err := json.Marshal(students) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } err = os.WriteFile("students.json" , data, 0666 ) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func main () { for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : addStudent() case 2 : getStudent() case 3 : getOneStudent() case 4 : updateStudent() case 5 : delStudentByIndex() case 6 : saveStudent() case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
数据初始化 核心:读取数据到 jsonBytes,然后 json.Unmarshal(jsonBytes, &students)
反序列化。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 package mainimport ( "encoding/json" "fmt" "io/ioutil" "os" ) type Student struct { ID int Name string Age int Email string Phone string } var students []Studentfunc addStudent () { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) students = append (students, Student{len (students) + 1 , name, age, email, phone}) } func getStudent () { for _, student := range students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func getIndexById () (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range students { if student.ID == id { index = i break } } return } func getOneStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func updateStudent () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func delStudentByIndex () { index := getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } students = append (students[:index], students[index+1 :]...) fmt.Println("删除成功" ) } func saveStudent () { data, err := json.Marshal(students) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } err = os.WriteFile("students.json" , data, 0666 ) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func initStudent () { jsonBytes, _ := ioutil.ReadFile("students.json" ) if len (jsonBytes) == 0 { return } err := json.Unmarshal(jsonBytes, &students) if err != nil { fmt.Println("无法读取文件:" , err) return } } func main () { initStudent() for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : addStudent() case 2 : getStudent() case 3 : getOneStudent() case 4 : updateStudent() case 5 : delStudentByIndex() case 6 : saveStudent() case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
面向对象 核心:准备 StuService 结构体,其他的方法都挂载到这个结构体上面。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 package mainimport ( "encoding/json" "fmt" "os" ) type Student struct { ID int Name string Age int Email string Phone string } type StuService struct { students []Student } func (s StuService) addStudent() { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) s.students = append (s.students, Student{ID: len (s.students) + 1 , Name: name, Age: age, Email: email, Phone: phone}) } func (s StuService) getAllStudent() { for _, student := range s.students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func (s StuService) getIndexById() (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range s.students { if student.ID == id { index = i break } } return } func (s StuService) getOneStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := s.students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func (s StuService) updateStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) s.students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) s.students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) s.students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) s.students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func (s StuService) delStudentByIndex() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } s.students = append (s.students[:index], s.students[index+1 :]...) fmt.Println("删除成功" ) } func (s StuService) saveStudent() { data, err := json.Marshal(s.students) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } err = os.WriteFile("students.json" , data, 0666 ) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func (s StuService) initStudent() { jsonBytes, _ := os.ReadFile("students.json" ) if len (jsonBytes) == 0 { fmt.Println("没有找到学生信息文件" ) return } err := json.Unmarshal(jsonBytes, &s.students) if err != nil { fmt.Println("无法读取文件:" , err) return } } func main () { stu := StuService{} stu.initStudent() for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : stu.addStudent() case 2 : stu.getAllStudent() case 3 : stu.getOneStudent() case 4 : stu.updateStudent() case 5 : stu.delStudentByIndex() case 6 : stu.saveStudent() case 7 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
问题解决 为什么添加完学生后看不到?
答案:结构体是值类型,像下面每次调用方法的时候发生了值拷贝。
1 2 3 4 5 6 stu.addStudent() stu.getAllStudent() stu := &StuService{} func (s *StuService) addStudent() {}
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 package mainimport ( "encoding/json" "fmt" "os" ) type Student struct { ID int Name string Age int Email string Phone string } type StuService struct { students []Student } func (s *StuService) addStudent() { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) s.students = append (s.students, Student{ID: len (s.students) + 1 , Name: name, Age: age, Email: email, Phone: phone}) } func (s *StuService) getAllStudent() { for _, student := range s.students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func (s *StuService) getIndexById() (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range s.students { if student.ID == id { index = i break } } return } func (s *StuService) getOneStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := s.students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func (s *StuService) updateStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) s.students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) s.students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) s.students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) s.students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func (s *StuService) delStudentByIndex() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } s.students = append (s.students[:index], s.students[index+1 :]...) fmt.Println("删除成功" ) } func (s *StuService) saveStudent() { data, err := json.Marshal(s.students) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } err = os.WriteFile("students.json" , data, 0666 ) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func (s *StuService) initStudent() { jsonBytes, _ := os.ReadFile("students.json" ) if len (jsonBytes) == 0 { fmt.Println("没有找到学生信息文件" ) return } err := json.Unmarshal(jsonBytes, &s.students) if err != nil { fmt.Println("无法读取文件:" , err) return } } func main () { stu := &StuService{} stu.initStudent() for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : stu.addStudent() case 2 : stu.getAllStudent() case 3 : stu.getOneStudent() case 4 : stu.updateStudent() case 5 : stu.delStudentByIndex() case 6 : stu.saveStudent() case 7 : fmt.Println("退出系统" ) default : 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 package mainimport ( "encoding/json" "fmt" "os" ) type Student struct { ID int Name string Age int Email string Phone string } type StuService struct { students []Student } func (s *StuService) addStudent() { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) s.students = append (s.students, Student{ID: len (s.students) + 1 , Name: name, Age: age, Email: email, Phone: phone}) } func (s *StuService) getAllStudent() { for _, student := range s.students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func (s *StuService) getIndexById() (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range s.students { if student.ID == id { index = i break } } return } func (s *StuService) getOneStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := s.students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func (s *StuService) updateStudent() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) s.students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) s.students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) s.students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) s.students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func (s *StuService) delStudentByIndex() { index := s.getIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } s.students = append (s.students[:index], s.students[index+1 :]...) fmt.Println("删除成功" ) } type FileService struct {} func (f *FileService) write(fromData []Student) { data, err := json.Marshal(fromData) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } err = os.WriteFile("students.json" , data, 0666 ) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func (f *FileService) read() []Student { var data []Student jsonBytes, _ := os.ReadFile("students.json" ) if len (jsonBytes) == 0 { fmt.Println("没有找到学生信息文件" ) return data } err := json.Unmarshal(jsonBytes, &data) if err != nil { fmt.Println("无法读取文件:" , err) } return data } func main () { stu := &StuService{} file := &FileService{} stu.students = file.read() for { fmt.Println(` ================================ ==== 1. 添加学生 ==== 2. 查询所有学生 ==== 3. 查询某个学生 ==== 4. 修改学生 ==== 5. 删除学生 ==== 6. 保存信息 ==== 7. 退出系统 ================================ ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : stu.addStudent() case 2 : stu.getAllStudent() case 3 : stu.getOneStudent() case 4 : stu.updateStudent() case 5 : stu.delStudentByIndex() case 6 : file.write(stu.students) case 7 : case 8 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
掌握接口使用 Go 语言提供了接口数据类型,接口就是把一些共性的方法集合在一起定义,不需要实现具体的方法内容。如果有结构体将接口定义的方法全部实现了,那么就代表实现了这个接口。Go 是隐式实现,A 结构体实现了 B 接口中的所有方法即可,不需要显示声明。
接口的目的:定标准!
基本操作 普通的结构体和方法。
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 package mainimport "fmt" type AliPay struct {}type WeixinPay struct {}func (a AliPay) pay() { fmt.Println("支付宝pay" ) } func (w WeixinPay) pay() { fmt.Println("微信pay" ) } func main () { var p = AliPay{} p.pay() var w = WeixinPay{} w.pay() }
接口演示。
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 30 31 32 package mainimport "fmt" type Pay interface { pay() string } type AliPay struct {}type WeixinPay struct {}func (a AliPay) pay() string { fmt.Println("支付宝pay" ) return "AliPay" } func (w WeixinPay) pay() string { fmt.Println("微信pay" ) return "WeixinPay" } func main () { var p Pay p = AliPay{} p.pay() p = WeixinPay{} p.pay() }
结构体(struct)实现了接口的全部方法就代表实现了这个接口(interface)。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package mainimport ( "fmt" ) type USB interface { input() output() } type Mouse struct { name string } func (mouse Mouse) output() { fmt.Println(mouse.name, "鼠标输出" ) } func (mouse Mouse) input() { fmt.Println(mouse.name, "鼠标输入" ) } func test (u USB) { u.input() u.output() } func main () { m1 := Mouse{name: "罗技" } test(m1) k1 := KeyBoard{name: "雷蛇" } test(k1) var usb USB usb = k1 fmt.Println(usb) } type KeyBoard struct { name string } func (key KeyBoard) output() { fmt.Println(key.name, "键盘输出" ) } func (key KeyBoard) input() { fmt.Println(key.name, "键盘输入" ) }
模拟多态 接口的实现类/结构体都拥有多态特性:除了自己本身还是他对应接口的类型。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package mainimport "fmt" type Animal interface { eat() sleep() } type Dog struct { name string } func (dog Dog) eat() { fmt.Println(dog.name, "--eat" ) } func (dog Dog) sleep() { fmt.Println(dog.name, "--sleep" ) } func main () { dog := Dog{name: "旺财" } dog.eat() dog.sleep() foo(dog) var animal Animal animal = dog foo(animal) } func foo (a Animal) { a.eat() a.sleep() }
空接口 空接口就是 any
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 30 31 32 33 34 35 36 37 38 39 40 41 42 package mainimport "fmt" type A interface {}type Dog struct { name string } type Cat struct { name string } func foo (a A) { fmt.Println(a) } func main () { var a1 A = Cat{name: "喵喵" } var a2 A = Dog{name: "旺财" } var a3 A = 1 var a4 A = "dva" fmt.Println(a1) fmt.Println(a2) fmt.Println(a3) fmt.Println(a4) foo(a1) foo(1 ) map1 := make (map [string ]interface {}) map1["name" ] = "dva" map1["age" ] = 18 fmt.Println(map1) s1 := make ([]any, 0 , 10 ) s1 = append (s1, 1 , "12312" , false , a1, a2) fmt.Println(s1) }
其他举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" func main () { var x interface {} s := "吕布" x = s fmt.Printf("type:%T value:%v\n" , x, x) i := 100 x = i fmt.Printf("type:%T value:%v\n" , x, x) b := true x = b fmt.Printf("type:%T value:%v\n" , x, x) }
接口嵌套 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package mainimport ( "fmt" ) type AA interface { test1() } type BB interface { test2() } type CC interface { AA BB test3() } type Dog struct {} func (dog Dog) test1() { fmt.Println("test1" ) } func (dog Dog) test2() { fmt.Println("test2" ) } func (dog Dog) test3() { fmt.Println("test3" ) } func main () { var dog Dog = Dog{} dog.test1() dog.test2() dog.test3() var a AA = dog a.test1() var b BB = dog b.test2() var c CC = dog c.test1() c.test2() c.test3() }
接口断言 被断言的对象必须是接口类型,否则会报错。
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 30 31 32 33 34 35 package mainimport "fmt" type I interface {}func testAsserts (i interface {}) { switch i.(type ) { case string : fmt.Println("变量为string类型" ) case int : fmt.Println("变量为int类型" ) case I: fmt.Println("变量为I类型" ) case nil : fmt.Println("变量为nil类型" ) case interface {}: fmt.Println("变量为interface{}类型" ) default : fmt.Println("未知类型" ) } } func main () { testAsserts("string" ) testAsserts(1 ) var i I var i2 I = 1 testAsserts(i) testAsserts(i2) }
type 定义类型和起别名 定义类型
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 30 package mainimport "fmt" type MyInt int func main () { var a MyInt = 20 var b int = 10 fmt.Println(int (a) + b) fmt.Printf("%T\n" , a) fmt.Printf("%T\n" , b) }
别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" ) func main () { type diyint = int var a diyint = 30 var b int = 10 var c = a + b fmt.Printf("%d\n" , c) }
接口应用 空接口作为函数的参数:使用空接口实现可以接收任意类型的函数参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func show (a interface {}) { fmt.Printf("type:%T value:%v\n" , a, a) } func main () { show("hello world" ) show(1 ) }
空接口作为 map 的值:使用空接口实现可以保存任意值的字典。
1 2 3 4 var student = make (map [string ]interface {})student["name" ] = "张飞" student["age" ] = 18 fmt.Println(student)
类型断言:一个接口的值(简称接口值)是由一个具体类型
和具体类型的值
两部分组成的。这两部分分别称为接口的动态类型
和动态值
,想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:
该语法返回两个参数,第一个参数是x
转化为T
类型后的变量,第二个值是一个布尔值,若为true
则表示断言成功,为false
则表示断言失败。举个例子:
1 2 3 4 5 6 7 8 var x interface {}x = "张飞" v, ok := x.(string ) if ok { fmt.Println(v) } else { fmt.Println("类型断言失败" ) }
学习包的管理 Go 程序首先在 GOROOT/src
目录中寻找包目录,如果没有找到,则会去 GOPATH/src
目录中继续找,比如 fmt 包是位于 GOROOT/src
目录的,那么它将会从该目录导入。导入包的时候,会从 GOPATH/src
去查找(注意,除了全局的环境变量,使用 GoLand 开发的时候也可以局部配置),需要使用包名.函数名
来使用。main 函数所在的包,必须是 main 包,表示程序的入口。一个目录下所有的 Go 文件的 package 必须同名。
包分类:标准库、第三方、自定义。
GOPATH 下导包 可以通过 go env 查看 GOPATH 地址,我电脑的 GOPATH 是 C:\Users\dangp\go。
1 2 go env -w GO111MODULE=off
main.go 可以放在任何地方,用 VSCode 运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( "xuego/hello/api" "xuego/hello/db" ) func main () { api.RestAPI1() api.RestAPI2() db.MysqlHandler() db.RedisHandler() }
C:\Users\dangp\go\src\xuego\hello\api\http.go
1 2 3 4 5 6 7 package apiimport "fmt" func RestAPI1 () { fmt.Println("restAPI1" ) }
C:\Users\dangp\go\src\xuego\hello\api\rpc.go
1 2 3 4 5 6 7 8 package apiimport "fmt" func RestAPI2 () { fmt.Println("restAPI2" ) }
C:\Users\dangp\go\src\xuego\hello\db\mysql.go
1 2 3 4 5 6 7 8 package dbimport "fmt" func MysqlHandler () { fmt.Println("mysqlHandler" ) }
C:\Users\dangp\go\src\xuego\hello\db\redis.go
1 2 3 4 5 6 7 package dbimport "fmt" func RedisHandler () { fmt.Println("redisHandler" ) }
导包的几种形式 1 2 3 4 5 6 import ( _ "math/rand" )
init 函数,在 main 方法执行之前执行,通过 init 函数,通常用来初始化一些全局变量,建立一些第三方的连接(数据库连接)、注册、检查、修复程序状态。init 函数可以有多个。
1. 如果导入了多个匿名包,按照 main 中导入包的顺序来进行执行。 2. 同一个包下的多个 Go 文件,都有 init 的情况下,按照文件排放顺序来执行对应的 init 函数。 3. 单个 Go 文件中的多个 init 是顺序执行的。 4. 等所有 Go 文件中的 init 函数执行完毕后,才会到 main 包。
重点掌握 GoMod 1 2 3 4 5 6 7 go env go env -w GO111MODULE=on go mod init
完成上面 GOPATH 的案例:
1 2 3 4 5 6 7 8 9 📦hello ┣ 📂api ┃ ┣ 📜http.go ┃ ┗ 📜rpc.go ┗ 📂db ┃ ┣ 📜mysql.go ┃ ┗ 📜redis.go ┣ go.mod ┣ main.go
设置包的代理
1 go env -w GOPROXY="https://goproxy.cn,direct"
如何更改模块路径 如果初始化时指定的模块路径不合适,可以随时修改。
1. 编辑 go.mod
文件,将 module
行改为新的模块路径。例如:
1 module github.com/username/xuego/hello
2. 更新项目中的所有导入路径,确保它们与新的模块路径一致。例如:
1 import "github.com/username/xuego/hello/api"
3. 运行以下命令更新依赖:
关于 go mod tidy 假如 main.go 中的代码如下:
1 2 3 4 5 6 7 8 9 10 package mainimport ( "fmt" "github.com/jinzhu/now" ) func main () { fmt.Println(now.BeginningOfMinute()) }
我可以在 go.mod 所在的目录执行命令 go mod tidy
来拉取包中依赖的第三方包。第三方的依赖都会放到 GOMODCACHE 对应的目录,即 GOPATH\pkg\mod
目录。
内置的 strings 包 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package mainimport ( "fmt" "strings" ) func main () { str := "dva, xxx" fmt.Println(str[0 ]) fmt.Println(strings.Contains(str, "x" )) fmt.Println(strings.ContainsAny(str, "xi" )) fmt.Println(strings.Count(str, "x" )) fileName := "20250301.mp3" if strings.HasPrefix(fileName, "2025" ) { fmt.Println("找到 2025 开头的文件:" , fileName) } if strings.HasSuffix(fileName, ".mp3" ) { fmt.Println("找到 mp3 结尾的文件:" , fileName) } fmt.Println(strings.Index(str, "v" )) fmt.Println(strings.LastIndex(str, "a" )) str2 := []string {"a" , "b" , "c" , "d" , "e" } fmt.Println(strings.Join(str2, "~" )) str3 := strings.Join(str2, "~" ) fmt.Println(strings.Split(str3, "~" )) fmt.Println(strings.ToUpper(str)) fmt.Println(strings.ToLower(str)) fmt.Println(strings.Replace(str, "x" , "🤠" , 1 )) str5 := str[0 :3 ] fmt.Println(str5) }
strconv 类型转换 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 30 31 32 33 34 35 36 37 38 39 package mainimport ( "fmt" "strconv" ) func main () { s1 := "true" b1, err := strconv.ParseBool(s1) if err != nil { fmt.Println(err) return } fmt.Printf("%T,%t\n" , b1, b1) s2 := strconv.FormatBool(b1) fmt.Printf("%T,%s\n" , s2, s2) s3 := "100000" i1, _ := strconv.ParseInt(s3, 10 , 64 ) fmt.Printf("%T,%d\n" , i1, i1) s4 := strconv.FormatInt(i1, 10 ) fmt.Printf("%T,%s\n" , s4, s4) atoi, _ := strconv.Atoi("-20" ) fmt.Printf("%T,%d\n" , atoi, atoi) itoa := strconv.Itoa(30 ) fmt.Printf("%T,%s\n" , itoa, itoa) }
常见内置包演示 time 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 package mainimport ( "fmt" "time" ) func main () { time1() } func time1 () { now := time.Now() year := now.Year() month := now.Month() day := now.Day() hour := now.Hour() minute := now.Minute() second := now.Second() fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n" , year, month, day, hour, minute, second) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "time" ) func main () { time2() } func time2 () { now := time.Now() fmt.Println(now.Format("2006-01-02 15:04:05" )) fmt.Println(now.Format("2006-01-02 03:04:05 PM" )) fmt.Println(now.Format("2006/01/02 15:04" )) fmt.Println(now.Format("15:04 2006/01/02" )) fmt.Println(now.Format("2006/01/02" )) }
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 package mainimport ( "fmt" "time" ) func main () { time3() } func time3 () { loc, err := time.LoadLocation("Asia/Shanghai" ) if err != nil { fmt.Println(err) return } timeStr := "2025-04-01 15:28:33" timeObj, _ := time.ParseInLocation("2006-01-02 15:04:05" , timeStr, loc) fmt.Println(timeObj) }
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 30 31 package mainimport ( "fmt" "time" ) func main () { time4() } func time4 () { now := time.Now() timestamp1 := now.Unix() timestamp2 := now.UnixNano() fmt.Println(timestamp1) fmt.Println(timestamp2) timeObj := time.Unix(timestamp1, 0 ) year := timeObj.Year() month := timeObj.Month() day := timeObj.Day() hour := timeObj.Hour() minute := timeObj.Minute() second := timeObj.Second() fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n" , year, month, day, hour, minute, second) }
随机数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport ( "fmt" "math/rand" ) func main () { num1 := rand.Int() fmt.Println("num1:" , num1) num2 := rand.Intn(100 ) fmt.Println("num2:" , num2) }
定时器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "time" ) func main () { ticker := time.Tick(time.Second) for i := range ticker { fmt.Println(i) } }
时间比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "time" ) func main () { now := time.Now() later := now.Add(time.Hour) fmt.Println(later) subTime := later.Sub(now) fmt.Println(subTime) fmt.Println(now.Equal(later)) fmt.Println(now.Before(later)) fmt.Println(now.After(later)) }
学生管理拆分包 main.go 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package mainimport ( . "cms/service" "fmt" ) func main () { stu := &StudentService{} file := &FileDBService{} stu.Students = file.Read() for { fmt.Println(` 1. 添加学生 2. 查询所有学生 3. 查询某个学生 4. 修改学生 5. 删除学生 6. 保存到文件 7. 保存到数据库 8. 退出系统 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : stu.AddStudent() case 2 : stu.GetAllStudent() case 3 : stu.GetOneStudent() case 4 : stu.UpdateStudent() case 5 : stu.DelStudentByIndex() case 6 : file.Write(stu.Students) case 7 : case 8 : fmt.Println("退出系统" ) default : fmt.Println("无效的选择,请重新输入" ) } } }
go.mod
service/db.go 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package serviceimport ( "cms/config" . "cms/model" "encoding/json" "fmt" "io/ioutil" "os" ) type FileDBService struct {} func (f *FileDBService) Write(fromData []Student) { file, err := os.Create(config.StudentJSONPath) if err != nil { fmt.Println("无法创建文件:" , err) return } defer file.Close() data, err := json.Marshal(fromData) if err != nil { fmt.Println("无法转换为 JSON:" , err) return } _, err = file.Write(data) if err != nil { fmt.Println("无法写入文件:" , err) return } fmt.Println("保存成功" ) } func (f *FileDBService) Read() []Student { var data []Student jsonBytes, _ := ioutil.ReadFile(config.StudentJSONPath) if len (jsonBytes) == 0 { fmt.Println("没有找到学生信息文件" ) return data } err := json.Unmarshal(jsonBytes, &data) if err != nil { fmt.Println("无法读取文件:" , err) } return data }
service/student.go 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 package serviceimport ( . "cms/model" "fmt" ) type StudentService struct { Students []Student } func (s *StudentService) AddStudent() { var name string var age int var email string var phone string fmt.Print("请输入学生姓名:" ) fmt.Scanln(&name) fmt.Print("请输入学生年龄:" ) fmt.Scanln(&age) fmt.Print("请输入学生邮箱:" ) fmt.Scanln(&email) fmt.Print("请输入学生电话:" ) fmt.Scanln(&phone) s.Students = append (s.Students, Student{ID: len (s.Students) + 1 , Name: name, Age: age, Email: email, Phone: phone}) } func (s *StudentService) GetAllStudent() { for _, student := range s.Students { fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } } func (s *StudentService) GetIndexById() (index int ) { var id int fmt.Print("请输入学生ID:" ) fmt.Scanln(&id) index = -1 for i, student := range s.Students { if student.ID == id { index = i break } } return } func (s *StudentService) GetOneStudent() { index := s.GetIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } student := s.Students[index] fmt.Printf("ID: %d, 姓名: %s, 年龄: %d, 邮箱: %s, 电话: %s\n" , student.ID, student.Name, student.Age, student.Email, student.Phone) } func (s *StudentService) UpdateStudent() { index := s.GetIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } fmt.Println(` 1. 修改姓名 2. 修改年龄 3. 修改邮箱 4. 修改电话 ` ) var choice int fmt.Print("请输入您的选择:" ) fmt.Scanln(&choice) switch choice { case 1 : fmt.Print("请输入新的姓名:" ) var name string fmt.Scanln(&name) s.Students[index].Name = name case 2 : fmt.Print("请输入新的年龄:" ) var age int fmt.Scanln(&age) s.Students[index].Age = age case 3 : fmt.Print("请输入新的邮箱:" ) var email string fmt.Scanln(&email) s.Students[index].Email = email case 4 : fmt.Print("请输入新的电话:" ) var phone string fmt.Scanln(&phone) s.Students[index].Phone = phone default : fmt.Println("无效的选择" ) } } func (s *StudentService) DelStudentByIndex() { index := s.GetIndexById() if index == -1 { fmt.Println("没有找到该学生" ) return } s.Students = append (s.Students[:index], s.Students[index+1 :]...) fmt.Println("删除成功" ) }
model/student.go 1 2 3 4 5 6 7 8 9 10 11 package modeltype Student struct { ID int Name string Age int Email string Phone string }
db/students.json 1 2 3 4 5 6 7 8 9 [ { "ID" : 1 , "Name" : "IFER" , "Age" : 18 , "Email" : "IFER@QQ.COM" , "Phone" : "18598279121" } ]
config/config.go 1 2 3 4 package configvar StudentJSONPath = "./db/students.json"
学习网络编程 基本操作 server/main.go,net.Listen
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 package mainimport ( "fmt" "net" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } fmt.Println(conn.RemoteAddr().String(), "有人连接成功" ) }
client/main.go,net.Dial
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "fmt" "net" ) func main () { conn, err := net.Dial("tcp" , "127.0.0.1:8888" ) if err != nil { fmt.Println(err) return } defer conn.Close() fmt.Println(conn.RemoteAddr().String(), "连接服务器连接成功" ) }
继续处理 server/main.go,conn.Read
接收消息
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 30 31 32 33 34 35 36 37 38 39 40 41 42 package mainimport ( "fmt" "net" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } defer conn.Close() data := make ([]byte , 1024 ) n, err := conn.Read(data) if err != nil { fmt.Println(err) return } fmt.Printf("接收到数据:%s\n" , string (data[:n])) _, err = conn.Write([]byte ("Hello Client" )) if err != nil { fmt.Println(err) return } }
client/main.go,conn.Write
发送消息
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 30 31 32 33 package mainimport ( "fmt" "net" ) func main () { conn, err := net.Dial("tcp" , "127.0.0.1:8888" ) if err != nil { fmt.Println(err) return } defer conn.Close() var word string fmt.Print("请输入要发送的数据:" ) fmt.Scanln(&word) conn.Write([]byte (word)) data := make ([]byte , 1024 ) n, err := conn.Read(data) if err != nil { fmt.Println(err) return } fmt.Printf("接收到数据:%s\n" , string (data[:n])) }
继续封装
服务端有两个阻塞,一个是连接阻塞,一个是 Read 阻塞。
server/main.go
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package mainimport ( "fmt" "net" "strings" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() for { conn, err := listenner.Accept() if err != nil { fmt.Println(err) continue } go ClientConn(conn) } } func ClientConn (conn net.Conn) { defer conn.Close() ipAddr := conn.RemoteAddr().String() buf := make ([]byte , 1024 ) for { n, err := conn.Read(buf) if err != nil { fmt.Println(err) return } result := buf[:n] fmt.Printf("接收到数据,来自[%s] [%d]:%s\n" , ipAddr, n, string (result)) if string (result) == "exit" { fmt.Println(ipAddr, "退出连接" ) return } conn.Write([]byte (strings.ToUpper(string (result)))) } }
client/main.go
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package mainimport ( "fmt" "net" ) func main () { conn, err := net.Dial("tcp" , "127.0.0.1:8888" ) if err != nil { fmt.Println(err) return } defer conn.Close() for { buf := make ([]byte , 1024 ) fmt.Printf("请输入发送的内容:" ) fmt.Scan(&buf) fmt.Printf("发送的内容:%s\n" , string (buf)) conn.Write(buf) res := make ([]byte , 1024 ) n, err := conn.Read(res) if err != nil { fmt.Println(err) return } result := res[:n] fmt.Printf("接收到数据:%s\n" , string (result)) } }
远程执行 SSH 命令 server/main.go
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package mainimport ( "fmt" "net" "os/exec" "github.com/axgle/mahonia" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() for true { conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } go ClientConn(conn) } } func ClientConn (conn net.Conn) { defer conn.Close() ipAddr := conn.RemoteAddr().String() fmt.Println(ipAddr, "连接成功" ) for true { data := make ([]byte , 1024 ) n, err := conn.Read(data) fmt.Println("命令字节数" , n) if err != nil { fmt.Println(err) return } data = data[:n] fmt.Printf("接收到命令,来自[%s] [%d]:%s\n" , ipAddr, n, string (data)) if string (data) == "exit" { fmt.Println(ipAddr, "退出连接" ) return } cmd := exec.Command("cmd" , "/C" , string (data)) output, err := cmd.Output() if err != nil { panic (err) } dec := mahonia.NewDecoder("gbk" ) _, cdata, _ := dec.Translate(output, true ) result := string (cdata) fmt.Println(result) conn.Write([]byte (string (result))) } }
client/main.go
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 30 31 32 33 34 35 36 37 38 39 40 41 42 package mainimport ( "bufio" "fmt" "net" "os" "strings" ) func main () { conn, err := net.Dial("tcp" , "127.0.0.1:8888" ) if err != nil { fmt.Println(err) return } defer conn.Close() for true { reader := bufio.NewReader(os.Stdin) fmt.Println("输入执行命令>>>" ) text, _ := reader.ReadString('\n' ) text = strings.TrimSpace(text) fmt.Println("text" , text) conn.Write([]byte (text)) res := make ([]byte , 100000 ) n, err := conn.Read(res) if err != nil { fmt.Println("err:" , err) return } fmt.Println("n" , n) result := res[:n] fmt.Printf("接收到数据:%s\n" , string (result)) } }
bash
最基础的 Web 服务 main.go
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 30 31 32 33 34 35 36 37 38 39 40 package mainimport ( "fmt" "net" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() for { conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } go ClientConn(conn) } } func ClientConn (conn net.Conn) { defer conn.Close() data := make ([]byte , 1024 ) n, _ := conn.Read(data) fmt.Println(string (data[:n])) conn.Write([]byte ("HTTP/1.1 200 ok\r\n\r\nHello World" )) }
此时浏览器输入 http://127.0.0.1:8888/
🤠 如何返回图片?
1 conn.Write([]byte ("HTTP/1.1 200 ok\r\n\r\n<h1>Hello World</h1><img src='http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960'/>" ))
把返回的内容抽离为单独的 index.html 文件。
1 2 <h1 > Hello World</h1 > <img src ="http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960" />
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 30 31 32 33 34 35 36 37 38 39 40 package mainimport ( "fmt" "io/ioutil" "net" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() for true { conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } go ClientConn(conn) } } func ClientConn (conn net.Conn) { defer conn.Close() data := make ([]byte , 1024 ) n, _ := conn.Read(data) fmt.Println(string (data[:n])) data, _ = os.ReadFile("index.html" ) conn.Write([]byte ("HTTP/1.1 200 ok\r\n\r\n" + string (data))) }
HTTP 协议 请求协议
响应格式
演示 Content-Type
1 2 conn.Write([]byte ("HTTP/1.1 200 ok\r\nContent-Type: text/plain\r\n\r\n" + string (data)))
根据 path 返回不同的资源,演示:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package mainimport ( "fmt" "io/ioutil" "net" "strings" ) func main () { listenner, err := net.Listen("tcp" , "0.0.0.0:8888" ) if err != nil { fmt.Println(err) return } defer listenner.Close() for true { conn, err := listenner.Accept() if err != nil { fmt.Println(err) return } go ClientConn(conn) } } func ClientConn (conn net.Conn) { defer conn.Close() data := make ([]byte , 1024 ) n, _ := conn.Read(data) requestStr := string (data[:n]) ret := strings.Split(requestStr, "\r\n" ) path := strings.Split(ret[0 ], " " )[1 ] if path == "/login" { conn.Write([]byte ("HTTP/1.1 200 ok\r\n\r\n" + "login ok" )) return } data, _ = ioutil.ReadFile("index.html" ) conn.Write([]byte ("HTTP/1.1 200 ok\r\nContent-Type: text/plain\r\n\r\n" + string (data))) }
Gin 框架基础 https://gin-gonic.com/zh-cn/
基础起步 初始化 1 2 3 go mod init quikestartgo get -u github.com/gin-gonic/gin
http://localhost:8080
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.GET("/" , func (c *gin.Context) { c.String(http.StatusOK, "Hello World" ) }) router.GET("/login" , func (c *gin.Context) { c.String(http.StatusOK, "login ok" ) }) router.Run(":8080" ) }
返回 HTML 文件 router.LoadHTMLGlob("templates/*")
和 c.HTML(http.StatusOK, "index.html", nil)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func home (c *gin.Context) { c.HTML(http.StatusOK, "index.html" , nil ) } func main () { router := gin.Default() router.LoadHTMLGlob("templates/*" ) router.GET("/" , home) router.Run(":8080" ) }
templates/index.html
返回 JSON 数据 c.JSON()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "github.com/gin-gonic/gin" ) func home (c *gin.Context) { c.JSON(200 , gin.H{ "message" : "hello world" , "data" : []string {"西游记" , "国色天香" }, }) } func main () { router := gin.Default() router.GET("/" , home) router.Run(":8080" ) }
接受 POST 数据 登录案例:客户端编码 Content-Type: application/x-www-form-urlencoded
,服务端 Gin 框架通过 c.PostForm() 函数接收数据。
main.go
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 30 31 32 33 34 35 package mainimport ( "github.com/gin-gonic/gin" ) func login (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func auth (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) if username == "admin" && password == "123456" { c.JSON(200 , gin.H{ "message" : "登录成功" , }) } else { c.JSON(200 , gin.H{ "message" : "用户名或密码错误" , }) } } func main () { router := gin.Default() router.LoadHTMLGlob("templates/*" ) router.GET("/login" , login) router.POST("/auth" , auth) router.Run(":8080" ) }
templates/login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > Title</title > </head > <body > <form action ="/auth" method ="post" > <label for ="username" > 用户名</label > <input type ="text" name ="username" id ="username" /> <br /> <label for ="password" > 密码:</label > <input type ="text" name ="password" id ="password" /> <br /> <input type ="submit" /> </form > </body > </html >
测试:http://127.0.0.1:8080/login
展示服务器时间 后端渲染模板和返回变量,c.HTML(200, "timer.html", gin.H{ "now": now, })
main.go
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 package mainimport ( "time" "github.com/gin-gonic/gin" ) func timer (c *gin.Context) { now := time.Now().Unix() c.HTML(200 , "timer.html" , gin.H{ "now" : now, }) } func main () { router := gin.Default() router.LoadHTMLGlob("templates/*" ) router.GET("/timer" , timer) router.Run(":8080" ) }
templates/timer.html
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Document</title > </head > <body > <h2 > {{.now}}</h2 > </body > </html >
路由定义 路由方法 RESTFul 风格路由
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package mainimport ( "github.com/gin-gonic/gin" ) func getUser (c *gin.Context) { c.JSON(200 , gin.H{ "message" : "hello world" , }) } func addUser (c *gin.Context) {} func getOneUser (c *gin.Context) {} func delOneUser (c *gin.Context) {} func putOneUser (c *gin.Context) {}func main () { router := gin.Default() router.LoadHTMLGlob("templates/*" ) router.POST("/user" , addUser) router.DELETE("/user/:id" , delOneUser) router.PUT("/user/:id" , putOneUser) router.GET("/user" , getUser) router.GET("/user/:id" , getOneUser) router.Any("/xxx" , func (context *gin.Context) { }) router.NoRoute(func (c *gin.Context) {}) router.Run(":8080" ) }
路由分组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "github.com/gin-gonic/gin" ) func main () { router := gin.Default() bookRouter := router.Group("/book" ) { bookRouter.GET("/" , func (context *gin.Context) {}) bookRouter.POST("/add" , func (context *gin.Context) {}) } publishRoute := router.Group("/publish" ) { publishRoute.GET("/" , func (context *gin.Context) {}) publishRoute.POST("/add" , func (context *gin.Context) {}) } router.Run(":8080" ) }
请求参数 获取请求信息 main.go
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 package mainimport ( "fmt" "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.GET("/test" , func (c *gin.Context) { fmt.Println(1 , c.Request.Method) fmt.Println(2 , c.Request.URL) fmt.Println(3 , c.Request.RemoteAddr) fmt.Println(4 , c.ClientIP()) fmt.Println(5 , c.Request.Header) fmt.Println(6 , c.Request.Header["User-Agent" ]) fmt.Println(7 , c.GetHeader("User-Agent" )) c.String(200 , "test OK!" ) }) router.Run(":8080" ) }
获取路径参数 http://127.0.0.1:8080/user/123
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.GET("/user/:id" , func (c *gin.Context) { id := c.Param("id" ) c.String(200 , id) }) router.Run(":8080" ) }
获取查询参数 http://127.0.0.1:8080/user?name=ifer
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 30 package mainimport ( "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.GET("/user" , func (c *gin.Context) { name, ok := c.GetQuery("name" ) if !ok { c.String(400 , "参数不存在" ) return } c.String(200 , name) }) router.Run(":8080" ) }
获取 PostForm c.PostForm 能解析 x-www-form-urlencoded 和 form-data 类型的数据。
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 package mainimport ( "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.POST("/user" , func (c *gin.Context) { name, ok := c.GetPostForm("name" ) if !ok { c.JSON(400 , gin.H{"error" : "name is required" }) return } c.JSON(200 , gin.H{"name" : name}) }) router.Run(":8080" ) }
PostFormArray
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport ( "github.com/gin-gonic/gin" ) func main () { router := gin.Default() router.POST("/user" , func (c *gin.Context) { names := c.PostFormArray("names" ) c.JSON(200 , gin.H{"names" : names}) }) router.Run(":8080" ) }
关于 ShouldBind 基于请求的 Content-Type 自动提取请求中 querystring、form 表单、JSON、XML 等参数到结构体中,⽀持 Get/Post 请求。
还有其他特定的方法,例如 c.ShouldBindJSON()、c.ShouldBindXML()、c.ShouldBindQuery()、c.ShouldBindYAML() 等。
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 30 31 32 33 34 35 36 37 package mainimport ( "log" "github.com/gin-gonic/gin" ) type User struct { Name string `json:"name" form:"name"` Age int `json:"age" form:"age"` Email string `json:"email" form:"email"` } func main () { r := gin.Default() r.POST("/user" , func (c *gin.Context) { user := User{} if c.ShouldBind(&user) == nil { log.Println("user:" , user) } c.JSON(200 , gin.H{ "msg" : "Parser Success" , "request content-type" : c.ContentType(), "data" : user, }) }) r.Run(":8080" ) }
响应数据 不同格式 字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport ( "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.GET("/user" , func (c *gin.Context) { c.String(200 , "Hello World" ) }) r.Run(":8080" ) }
HTML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.LoadHTMLGlob("templates/*" ) r.GET("/user" , func (c *gin.Context) { c.HTML(http.StatusOK, "index.html" , nil ) }) r.Run(":8080" ) }
JSON 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.GET("/user" , func (c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message" : "Hello, user!" , }) }) r.Run(":8080" ) }
XML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.GET("/user" , func (c *gin.Context) { c.XML(http.StatusOK, gin.H{ "message" : "Hello, XML!" , }) }) r.Run(":8080" ) }
静态文件 http://127.0.0.1:8080/static/img.png
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.Static("/static" , "./static" ) r.Run(":8080" ) }
重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.GET("/user" , func (c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/index" ) }) r.GET("/index" , func (c *gin.Context) { c.String(http.StatusOK, "Hello World!" ) }) r.Run(":8080" ) }
模板定义 渲染取值 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 30 31 32 33 34 35 36 37 38 package mainimport "github.com/gin-gonic/gin" type Student struct { Name string Age int } func main () { r := gin.Default() r.LoadHTMLGlob("templates/*" ) r.GET("/index" , func (context *gin.Context) { context.HTML(200 , "index.html" , gin.H{ "user" : "IFER" , "addressSlice" : []string {"上海" , "深圳" , "武汉" }, "stuMap" : map [string ]interface {}{ "name" : "ELSER" , "age" : 18 , "hobby" : []string {"吃饭" , "睡觉" , "打豆豆" }, }, "stuStruct" : Student{Name: "IFER" , Age: 22 }, "stuSlice" : []Student{{Name: "IFER" , Age: 22 }, {Name: "ELSER" , Age: 20 }}, }) }) r.Run() }
index.html
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > Document</title > </head > <body > <h3 > 变量渲染</h3 > {{ . }} <p > {{.addressSlice}}</p > <p > {{index .addressSlice 0 }}</p > <p > {{index .addressSlice 1}}</p > <p > {{index .addressSlice 2}}</p > <p > {{.stuMap}}</p > <p > {{.stuMap.name}}</p > <p > {{.stuStruct}}</p > <p > {{.stuStruct.Name}}</p > <p > {{$s := index .stuSlice 0}}</p > <p > {{$s.Name}}</p > </body > </html >
条件循环 条件判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> {{ if gt .stuStruct.Age 18 }} <h1>成年</h1> {{ else }} <h2>未成年</h2> {{ end }} </body> </html>
循环语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <!-- 循环结构体的切片 --> {{ range $index, $stu := .stuSlice }} <p>{{$index}} - {{$stu.Name}}</p> {{ end }} </body> </html>
其他操作
模板函数、嵌套和继承 …
学习 GORM https://gorm.io/
https://www.kancloud.cn/sliver_horn/gorm
1 2 go get -u gorm.io/gorm go get -u gorm.io/driver/mysql
连接数据库 基础语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main () { dsn := "username:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) fmt.Println(db, err) }
配置日志 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 30 31 32 package mainimport ( "fmt" "log" "os" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) func main () { dsn := "root:123456@tcp(127.0.0.1:3306)/go?charset=utf8mb4&parseTime=True&loc=Local" newLogger := logger.New( log.New(os.Stdout, "\r\n" , log.LstdFlags), logger.Config{ LogLevel: logger.Info, }, ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: newLogger, }) if err != nil { panic ("failed to connect database" ) } fmt.Println(db) }
关于表操作 模型声明 🤔 选课系统。
学生(多):班级(一)、学生(多):课程(多)
老师(一):班级(多)、老师(一):课程(多)
班级
课程
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 30 package mainimport ( "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" "log" "os" "time" ) type BaseModel struct { ID int `gorm:"primaryKey"` CreateTime *time.Time `gorm:"autoCreateTime"` UpdateTime *time.Time `gorm:"autoCreateTime"` } type Teacher struct { BaseModel Name string `gorm:"type:varchar(32);unique;not null"` Tno int Pwd string `gorm:"type:varchar(100);not null"` Tel string `gorm:"type:char(11);"` Birth *time.Time Remark string `gorm:"type:varchar(255);"` }
模型迁移 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package mainimport ( "fmt" "log" "os" "time" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) type BaseModel struct { ID int `gorm:"primaryKey"` CreateTime *time.Time `gorm:"autoCreateTime"` UpdateTime *time.Time `gorm:"autoCreateTime"` } type Teacher struct { BaseModel Name string `gorm:"type:varchar(32);unique;not null"` Tno int Pwd string `gorm:"type:varchar(100);not null"` Tel string `gorm:"type:char(11);"` Birth *time.Time Remark string `gorm:"type:varchar(255);"` } func main () { dsn := "root:123456@tcp(127.0.0.1:3306)/go?charset=utf8mb4&parseTime=True&loc=Local" newLogger := logger.New( log.New(os.Stdout, "\r\n" , log.LstdFlags), logger.Config{ LogLevel: logger.Info, }, ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: newLogger, }) if err != nil { panic ("failed to connect database" ) } db.AutoMigrate(&Teacher{}) fmt.Println(db) }
运行
其他表迁移,以及如何表示表之间的关系(在多的表中进行操作)。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package mainimport ( "fmt" "log" "os" "time" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) type BaseModel struct { ID int `gorm:"primaryKey"` CreateTime *time.Time `gorm:"autoCreateTime"` UpdateTime *time.Time `gorm:"autoCreateTime"` } type Teacher struct { BaseModel Name string `gorm:"type:varchar(32);unique;not null"` Tno int Pwd string `gorm:"type:varchar(100);not null"` Tel string `gorm:"type:char(11);"` Birth *time.Time Remark string `gorm:"type:varchar(255);"` } type Class struct { BaseModel Name string `gorm:"type:varchar(32);unique;not null"` Num int TeacherID int Teacher Teacher } type Course struct { BaseModel Name string `gorm:"type:varchar(32);unique;not null"` Credit int Period int TeacherID int Teacher Teacher } type Student struct { BaseModel Sno int Name string `gorm:"type:varchar(32);not null"` Pwd string `gorm:"type:varchar(100);not null"` Tel string `gorm:"type:char(11);"` Gender byte `gorm:"default:1"` Birth *time.Time Remark string `gorm:"type:varchar(255);"` ClassID int Class Class `gorm:"foreignKey:ClassID"` Courses []Course `gorm:"many2many:student2course;constraint:OnDelete:CASCADE;"` } func main () { dsn := "root:123456@tcp(127.0.0.1:3306)/go?charset=utf8mb4&parseTime=True&loc=Local" newLogger := logger.New( log.New(os.Stdout, "\r\n" , log.LstdFlags), logger.Config{ LogLevel: logger.Info, }, ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: newLogger, }) if err != nil { panic ("failed to connect database" ) } db.AutoMigrate(&Teacher{}) db.AutoMigrate(&Course{}) db.AutoMigrate(&Class{}) db.AutoMigrate(&Student{}) fmt.Println(db) }
表记录添加 添加记录 db.Create()
1 2 3 4 5 t1 := Teacher{Name: "IFER" , Tno: 1001 , Pwd: "123" } db.Create(&t1) fmt.Println(t1.ID)
批量添加多对一关联表的记录 班级
1 2 3 4 5 6 7 8 9 10 class01 := Class{Name: "软件1班" , TeacherID: 1 } class02 := Class{Name: "软件2班" , TeacherID: 1 } class03 := Class{Name: "计算机科学与技术1班" , TeacherID: 2 } class04 := Class{Name: "计算机科学与技术2班" , TeacherID: 2 } classes := []Class{class01, class02, class03, class04} db.Create(&classes) for _, class := range classes { fmt.Println(class.ID) }
课程
1 2 3 4 5 6 7 course01 := Course{Name: "计算机网络" , Credit: 3 , Period: 16 , TeacherID: 1 } course02 := Course{Name: "数据结构" , Credit: 2 , Period: 24 , TeacherID: 1 } course03 := Course{Name: "数据库" , Credit: 2 , Period: 16 , TeacherID: 2 } course04 := Course{Name: "数字电路" , Credit: 3 , Period: 12 , TeacherID: 2 } course05 := Course{Name: "模拟电路" , Credit: 1 , Period: 8 , TeacherID: 2 } courses := []Course{course01, course02, course03, course04, course05} db.Create(&courses)
添加多对多关联表的记录 两种方式:创建学生的同时绑定多对多、查询学生绑定多对多关系。
1 2 3 4 5 6 7 8 9 10 11 12 var courses []Coursedb.Where("name in ?" , []string {"数据结构" , "数据库" }).Find(&courses) s1 := Student{ Name: "张三" , Pwd: "123456" , ClassID: 1 , Courses: courses, } db.Create(&s1) fmt.Println(s1.Class.Name)
1 2 3 4 5 6 7 8 9 10 11 var courses []Coursedb.Where("name in ?" , []string {"计算机网络" , "数字电路与逻辑设计" }).Find(&courses) s1 := Student{ Name: "李四" , Pwd: "123456" , ClassID: 1 , } db.Create(&s1) db.Model(&s1).Association("Courses" ).Append(courses)
查询学生绑定多对多关系。
1 2 3 4 5 6 var courses []Coursedb.Where("name in ?" , []string {"计算机网络" , "数字电路与逻辑设计" }).Find(&courses) var student = Student{}db.Where("name = ?" , "赵六" ).First(&student) db.Model(&student).Association("Courses" ).Append(courses)
解除多对多的关系。
1 2 3 4 5 6 var courses []Coursedb.Where("name in ?" , []string {"计算机网络" , "数字电路与逻辑设计" }).Find(&courses) var student = Student{}db.Where("name = ?" , "赵六" ).First(&student) db.Model(&student).Association("Courses" ).Delete(courses)
查询张三选修了哪些课程。
1 2 3 4 5 6 7 8 var student Studentdb.Where("name = ?" , "张三" ).First(&student) var courses []Coursedb.Model(&student).Association("Courses" ).Find(&courses) for _, course := range courses { fmt.Printf("- %s (学分: %d, 学时: %d)\n" , course.Name, course.Credit, course.Period) }
表记录查询 查询全部记录 1 2 3 4 5 6 7 8 9 teachers := []Teacher{} db.Find(&teachers) for _, teacher := range teachers { fmt.Printf("教师ID: %d, 教师姓名: %s\n" , teacher.ID, teacher.Name) }
查询单条记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 course := Course{} db.Where("credit < ?" , 3 ).Order("credit asc" ).Take(&course) fmt.Println(course)
Where 查询 1 2 3 course := []Course{} db.Where("credit > ?" , 3 ).Find(&course) fmt.Println(course)
1 2 3 var total int64 db.Model(&Course{}).Where("credit between ? and ?" , 1 , 3 ).Count(&total) fmt.Println(total)
表记录删除 1 2 3 var course Coursedb.Where("name = ?" , "模拟电路" ).Take(&course) db.Delete(&course)
表记录更新 1 2 3 4 var course Coursedb.Where("name = ?" , "数字电路" ).Find(&course) course.Name = "数字电路与逻辑设计" db.Save(&course)
1 2 db.Model(&Course{}).Where("1=1" ).Update("Credit" , 3 )
关联表查询 查询软件 1 班班主任的名字。
1 2 3 var class Classdb.Preload("Teacher" ).Where("name = ?" , "软件1班" ).Find(&class) fmt.Println(class.Teacher.Name)
查询赵六所在班级的人数。
1 2 3 var student Studentdb.Preload("Class" ).Where("name = ?" , "赵六" ).Find(&student) fmt.Println(student.Class.Num)
查询张三的班级的名称和选修课程的名字及课程学分。
1 2 3 4 5 var student Studentdb.Preload("Class" ).Preload("Courses" ).Where("name = ?" , "张三" ).Find(&student) for _, course := range student.Courses { fmt.Println(course.Name, course.Credit) }
嵌套预加载:查询张三所在班级的班主任(老师)的名字。
1 2 3 var student Studentdb.Preload("Class" ).Preload("Class.Teacher" ).Where("name = ?" , "张三" ).Find(&student) fmt.Println(student.Class.Teacher.Name)
反向查询:软件 2 班有哪些学生。
1 2 3 4 5 6 7 8 9 10 11 12 13 var class Classdb.Preload("Students" ).Where("name = ?" , "软件1班" ).Find(&class) for _, student := range class.Students { fmt.Println(student.Name) }
一些代码备份 错误处理 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 30 31 32 type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf("错误码:%d,错误信息:%s" , e.Code, e.Message) } func doSomething () error { return &MyError{ Code: 404 , Message: "资源未找到" , } } func main () { if err := doSomething(); err != nil { fmt.Println(err) if myErr, ok := err.(*MyError); ok { fmt.Printf("错误码:%d\n" , myErr.Code) } } }
make 函数 如果只声明指针、切片、map、通道,那么默认都是 nil,不能直接操作,所以需要使用 make 来初始化或字面量初始化,如 s = make([]int, 5)
或 s := []int{1, 2, 3}
,映射声明后也为 nil,需要使用 make 函数或映射字面量初始化才能使用,如 var m map[string]int; m = make(map[string]int)
或 m := map[string]int{"key": "value"}
。通道同样需要使用 make 函数初始化,如 var ch chan int; ch = make(chan int)
。
指针接收者与值接收者 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type Counter int func (c Counter) Increment() Counter { return c + 1 } func (c *Counter) Add(val int ) { *c += Counter(val) } func main () { var c Counter = 5 fmt.Println(c.Increment()) fmt.Println(c) c.Add(10 ) fmt.Println(c) }