以下内容参考了
Go语言圣经gopl-zh.github.io/index.html
安装
下载地址:studygolang.com/dl
error code 2503
在win下安装出现
用管理员身份运行即可
$ go version
go version go1.22.0 windows/amd64
配置
镜像源
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
模块
更新或初始化 Go Module
go mod init <module-name>
这将创建一个 go.mod 文件,标志着项目现在是一个 Go 模块。
在
src目录下先创建一个文件夹,在文件夹下执行init。
$ >go mod init yhq
go: creating new go.mod: module yhq
go: to add module requirements and sums:
go mod tidy
goimports
$ >go install golang.org/x/tools/cmd/goimports@latest
程序结构
命名
Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,不能以数字开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:yhq和Yhq是两个不同的名字。
Go语言中类似if和switch的关键字有25个;关键字不能用于自定义名字,只能在特定语法结构中使用。
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
此外,还有大约30多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
内建常量: true false iota nil
内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内建函数: make len cap new append copy close delete
complex real imag
panic recover
这些内部预先定义的名字并不是关键字,可以在定义中重新使用它们。
package main
import (
"fmt"
)
func main() {
true := 666
fmt.Print(true) // 666
}
在一些特殊的场景中重新定义它们也是有意义的,但是也要注意避免过度而引起语义混乱。
如果一个名字是在函数内部定义,那么它就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。
名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(必须是在函数外定义的包级名字,包级函数名本身也是包级名字),那么它将是公开的,也就是public,如果是小写字母开头的,则是private。
变量
package main
import "fmt"
// 全局变量
var gb = 666
var (
gb1 = 777
gb2 = 888
)
func main() {
// 单个变量声明
// 如果没有如果初始化表达式被省略,那么将用零值初始化该变量。 ,
// `数值类型`变量对应的零值是`0`,`布尔类型`变量对应的零值是`false`,`字符串类型`对应的零值是`空字符串`
// `接口或引用类型`(包括slice、指针、map、chan和函数)变量对应的零值是`nil`
var a int
//类型推导
var b = 1
// 只能在函数中使用
c := 2
// 多变量声明
var d, e, f int
var g, h, i = 3, "s", 5
j, k, l := 6, "q", 8
fmt.Println(a, b, c, d, e, f, g, h, i, j, k, l, gb, gb1, gb2)
}
注意事项
敲黑板:
:=是一个变量声明语句,=是一个变量赋值操作。
i, j = j, i // 交换 i 和 j 的值
敲黑板:简短变量声明左边的变量可能并不是全部刚刚声明的。如果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过的变量就只有赋值行为了。
in, err := os.Open(infile) //对 in err 声明
out, err := os.Create(outfile) // 对 out 声明 对 err 赋值
简短变量声明语句中
必须至少要声明一个新的变量
f, err := os.Open(infile)
f, err := os.Create(outfile) // compile error: no new variables
//这段代码无法编译通过
解决的方法是第二个简短变量声明语句改用普通的多重赋值语句。
指针
一个变量对应一个保存了变量对应类型值的内存空间。普通变量在声明语句创建时被绑定到一个变量名,比如叫yhq的变量,但是还有很多变量始终以表达式方式引入,例如yhq[i]或yhq.y变量。所有这些表达式一般都是读取一个变量的值,如果它们出现在赋值语句的左边,这种时候是给对应变量赋予一个新的值。
一个指针对应变量在内存中的存储位置。并不是每一个值都会有一个内存地址,但是对于每一个变量必然有对应的内存地址。通过指针可以直接读或更新对应变量的值,而不需要知道该变量的名字。
如果用var x int声明一个x变量,那么&x表达式(取x变量的内存地址)将产生一个指向该整数变量的指针,指针对应的数据类型是*int,指针被称之为指向int类型的指针。如果指针名字为p,那么可以说p指针指向变量x,或者说p指针保存了x变量的内存地址。同时*p表达式对应p指针指向的变量的值。一般*p表达式读取指针指向的变量的值,这里为int类型的值,同时因为*p对应一个变量,所以该表达式也可以出现在赋值语句的左边,表示更新指针所指向的变量的值。
package main
import "fmt"
func main() {
x := 1 //声明一个变量
p := &x // &x代表指针 这时候p也是指针
fmt.Println(*p) // 1 *p的方式找到对应内存的值
fmt.Println(p) // 0xc000100038 直接输出是内存的地址
*p = 2 // 将内存地址的值改变
fmt.Println(x) // "2"
}
敲黑板:任何类型的指针的零值都是nil。
var x *int
fmt.Print(x == nil) // true
指针之间也是可以进行相等测试的,只有当它们指向同一个变量或全部是nil时才相等。
var x, y int
fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
类型
变量或表达式的类型定义了对应存储值的属性特征,例如数值在内存的存储大小(或者是元素的bit个数),它们在内部是如何表达的,是否支持一些操作符,以及它们自己关联的方法集等。
在任何程序中都会存在一些变量有着相同的内部结构,但是却表示完全不同的概念。例如,一个int类型的变量可以用来表示一个循环的迭代索引、或者一个时间戳、或者一个文件描述符、或者一个月份;一个float64类型的变量可以用来表示每秒移动几米的速度、或者是不同温度单位下的温度;一个字符串可以用来表示一个密码或者一个颜色的名称。
一个类型声明语句创建了一个新的类型名称,和现有类型具有相同的底层结构。新命名的类型提供了一个方法,用来分隔不同概念的类型,这样即使它们底层类型相同也是不兼容的。
type 类型名字 底层类型
类型声明语句一般出现在包一级,因此如果新创建的类型名字的首字符大写,则在包外部也可以使用。
// Package tempconv performs Celsius and Fahrenheit temperature computations.
package tempconv
import "fmt"
type Celsius float64 // 摄氏温度
type Fahrenheit float64 // 华氏温度
const (
AbsoluteZeroC Celsius = -273.15 // 绝对零度
FreezingC Celsius = 0 // 结冰点温度
BoilingC Celsius = 100 // 沸水温度
)
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
在这个包声明了两种类型:Celsius和Fahrenheit分别对应不同的温度单位。它们虽然有着相同的底层类型float64,但是它们是不同的数据类型,因此它们不可以被相互比较或混在一个表达式运算。刻意区分类型,可以避免一些像无意中使用不同单位的温度混合计算导致的错误。因此需要一个类似Celsius(t)或Fahrenheit(t)形式的显式转型操作才能将float64转为对应的类型。Celsius(t)和Fahrenheit(t)是类型转换操作,它们并不是函数调用。类型转换不会改变值本身,但是会使它们的语义发生变化。另一方面,CToF和FToC两个函数则是对不同温度单位下的温度进行换算,它们会返回不同的值。
// Package tempconv performs Celsius and Fahrenheit temperature computations.
package main
import "fmt"
type Celsius float64 // 摄氏温度
type Fahrenheit float64 // 华氏温度
const (
AbsoluteZeroC Celsius = -273.15 // 绝对零度
FreezingC Celsius = 0 // 结冰点温度
BoilingC Celsius = 100 // 沸水温度
)
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
func main() {
var c Celsius
var f Fahrenheit
fmt.Println(c == 0) // "true"
fmt.Println(f >= 0) // "true"
//fmt.Println(c == f) // compile error: type mismatch
fmt.Println(c == Celsius(f)) // "true"
}
包和文件
Go语言中的包和其他语言的库或模块的概念类似,目的都是为了支持模块化、封装、单独编译和代码重用。一个包的源代码保存在一个或多个以.go为文件后缀名的源文件中,通常一个包所在目录路径的后缀是包的导入路径;例如包gopl.io/ch1/helloworld对应的目录路径是$GOPATH/src/gopl.io/ch1/helloworld。
每个包都对应一个独立的名字空间。例如,在image包中的Decode函数和在unicode/utf16包中的 Decode函数是不同的。要在外部引用该函数,必须显式使用image.Decode或utf16.Decode形式访问。
test/change/tempconv.go
package change
type Celsius float64
type Fahrenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreezingC Celsius = 0
BoilingC Celsius = 100
)
test/change/conv.go
package change
// CToF converts a Celsius temperature to Fahrenheit.
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
// FToC converts a Fahrenheit temperature to Celsius.
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
test/main.go
package main
import (
"fmt"
"test/change"
)
func main() {
fmt.Println(change.CToF(change.AbsoluteZeroC))
fmt.Println(change.CToF(change.BoilingC))
}
运算符
package main
import "fmt"
func main() {
// 除法需要注意
var c float32 = 10 / 4
fmt.Print(c) // 2 只会保留整数部分,不会四舍五入
var a float32 = 10.0
c = a / 4
fmt.Print(c) // 2.5
}
基础数据类型
整型
| 类型 | 描述 |
|---|---|
| uint8 | 无符号 8 位整型 (0 到 255) |
| uint16 | 无符号 16 位整型 (0 到 65535) |
| uint32 | 无符号 32 位整型 (0 到 4294967295) |
| uint64 | 无符号 64 位整型 (0 到 18446744073709551615) |
| int8 | 有符号 8 位整型 (-128 到 127) |
| int16 | 有符号 16 位整型 (-32768 到 32767) |
| int32 | 有符号 32 位整型 (-2147483648 到 2147483647) |
| int64 | 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
package main
import (
"fmt"
"unsafe"
)
func main() {
// 查看数据类型和字节数
var s = 100 // 整型默认是int
fmt.Printf("s的类型是 %T 字节数是 %d", s, unsafe.Sizeof(s)) //s的类型是 int 字节数是 8
}
浮点型
| 类型 | 描述 |
|---|---|
| float32单精度 | IEEE-754 32位浮点型数 |
| float64双精度 | IEEE-754 64位浮点型数 |
package main
import (
"fmt"
)
func main() {
// 浮点类型默认是 float64
var a float32 = 666.777
var b float32 = -123.321
// 666.777 -123.321
fmt.Print(a, b)
var c float32 = 123.0000999
var d float64 = 123.0000999
// 123.0001 123.0000999
fmt.Print(c, d)
}
布尔型
布尔型的值只可以是常量 true 或者 false。,默认为 false。
布尔类型不会隐式的转为
0或1
package main
import "fmt"
func main() {
var a bool
var b bool = true
fmt.Println(a)
// false
fmt.Println(b)
// true
}
字符串
字符串就是一串固定长度的字符连接起来的字符序列。默认为""(空字符串)。字符串之间可以使用+拼接。
package main
import "fmt"
func main() {
a := "abcdefg"
fmt.Print(a[0]) // 97
fmt.Print(a[2:]) // cdefg
//字符串拼接
var s = "你好" + "啊"
s += "!"
fmt.Print(s)
//长文本
var long = `
啊
是
的
`
fmt.Print(long)
// 多个拼接 最后要是 +
var sss = "啊啊" + "啊啊" + "啊啊" +
"啊啊" + "啊啊" +
"啊啊"
fmt.Print(sss)
}
类型转换
Go在不同类型的变量之间赋值时需要显示转换。
package main
import "fmt"
func main() {
var s float32 = 12.12
var ss float64 = float64(s)
fmt.Printf("%T", ss) // float64
var a int64 = 5
var aa int8
var aaa int8
aa = int8(a) + 127 // 能编译通过,但是会溢出
aaa = int8(a) + 128 //无法通过编译
fmt.Print(aa, aaa)
}
package main
import "fmt"
func main() {
// 基本类型转字符串
var s int = 6
var ss float32 = 12.12
var b bool = true
var str string
str = fmt.Sprintf("%d", s)
fmt.Printf("%T", str)
str = fmt.Sprintf("%f", ss)
fmt.Printf("%T", str)
str = fmt.Sprintf("%t", b)
fmt.Printf("%T", str)
}
常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
const identifier [type] = value
可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。
- 显式类型定义:
const b string = "abc" - 隐式类型定义:
const b = "abc"
多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
package main
import "fmt"
func main() {
const LENGTH = 10
const WIDTH = 5
var area int
// 多重赋值
const a, b, c = 1, false, "str"
area = LENGTH * WIDTH
fmt.Printf("面积为: %d", area)
// 面积为: 50
fmt.Println(a, b, c)
// 1 false str
// 枚举
const (
A = 100
B = 90
C = 80
)
fmt.Println(A, B, C)
// 100 90 80
}
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值:
package main
import "fmt"
func main() {
const (
a = iota
b = iota
c = iota
)
fmt.Print(a, b, c)
// 0 1 2
// 简写
const (
A = iota
B
C
)
fmt.Println(A, B, C)
// 0 1 2
const (
AA = iota // 0
BB // 1
CC // 2
DD = "yhq" // yhq
EE // yhq 没有指定值,继承上一个值
FF = 100 // 100
GG // 100 没有指定值,继承上一个值
HH = iota // 7 恢复计数,const中每新增一行常量声明将使 iota 计数一次
II // 8
)
fmt.Println(AA, BB, CC, DD, EE, FF, GG, HH, II)
}
package main
import "fmt"
func main() {
const (
i = 1 << iota //等同于 i = 1<<0
j = 3 << iota //等同于 j = 3<<1
k //等同于 k = 3<<2
l //等同于 l = 3<<3
)
fmt.Println(i, j, k, l)
// 1 6 12 24
}
数组
在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说
[5]int和[10]int是不同的类型。
package main
import "fmt"
func main() {
//没有明确值的,不能使用 = 赋值
var arr1 [5]int //不能使用 var arr1 =[5]int
var arr2 = [3]int{1, 3, 5}
var arr3 = [...]int{1, 2, 3, 4, 5}
var more [4][5]int
fmt.Println(arr1, arr2, arr3, more)
// [0 0 0 0 0] [1 3 5] [1 2 3 4 5] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
fmt.Println(len(more)) // 4
for i := 0; i < len(more); i++ {
for j := 0; j < len(more[i]); j++ {
fmt.Print(more[i][j])
}
fmt.Print("\n")
}
/*
00000
00000
00000
00000
*/
for i := 0; i < len(arr2); i++ {
fmt.Println(arr2[i]) // 1 3 5
}
for i, v := range arr2 {
fmt.Println(i) // 0 1 2
fmt.Println(v) // 1 3 5
fmt.Println(arr2[i]) // 1 3 5
}
}
Slice
Slice本身没有数据,是对
底层array的一个view。
package main
import "fmt"
func main() {
// 创建Slice
var slice []int
fmt.Print(slice == nil) //true
for i := 0; i < 5; i++ {
slice = append(slice, 2*i+1)
}
fmt.Println(slice) // [1 3 5 7 9]
var new_slice = []int{1, 2, 3}
fmt.Println(new_slice) // [1 2 3]
var make_slice = make([]int, 16)
fmt.Println(make_slice) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
var make_slice_cap = make([]int, 16, 32)
fmt.Println(make_slice_cap) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
// 通过数组得到
var arr = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Print(arr[2:5]) // [2 3 4]
fmt.Print(arr[:5]) // [0 1 2 3 4]
fmt.Print(arr[1:]) // [1 2 3 4 5 6 7 8 9]
fmt.Print(arr[:]) // [0 1 2 3 4 5 6 7 8 9]
//修改
arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := arr[2:5]
fmt.Println(s1) // [2 3 4]
editSlice(s1)
fmt.Println(s1) // [100 3 4]
s2 := arr[3:]
fmt.Println(s2) // [3 4 5 6 7 8 9]
editSlice(s2)
fmt.Println(s2) // [100 4 5 6 7 8 9]
fmt.Println(arr) // [0 1 100 100 4 5 6 7 8 9]
}
func editSlice(s []int) {
s[0] = 100
}
扩展
Slice可以向后扩展,不能向前扩展。 s[i] 不可以超过 len(s),向后扩展不能超过底层数组 cap(s)
package main
import "fmt"
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
fmt.Print(s1) // [2 3 4 5]
s2 := s1[3:5]
fmt.Print(s2) // [5 6]
s3 := s2[1:]
s4 := s2[1:3]
fmt.Print(s3) // [6]
fmt.Print(s4) // [6 7]
fmt.Printf("s3=%v,len(s3)=%v,cap(s3)=%v", s3, len(s3), cap(s3)) // s3=[6],len(s3)=1,cap(s3)=2
}
append()
- 添加元素时如果超过
cap,系统会分配更大的底层数组 - 由于值传递的关系,必须接收append的返回值
package main
import "fmt"
func main() {
var arr = []int{0, 1, 2, 3, 4, 5, 6, 7}
s2 := s1[3:5]
fmt.Print(s2) // [5 6]
s3 := append(s2, 10) // [5 6 10]
s4 := append(s3, 11) // [5 6 10 11]
s5 := append(s4, 12) // [5 6 10 11 12]
fmt.Print(arr) // [0 1 2 3 4 5 6 10]
}
copy
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4}
s2 := make([]int, 5)
copy(s2, s1)
fmt.Print(s2) // [1 2 3 4 0]
s3 := []int{11, 12, 13, 14, 15, 16}
// copy时超过
copy(s2, s3)
fmt.Print(s1, s2) // [11 12 13 14 15]
}
取值
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := append(s1[:2], s1[3:]...)
fmt.Print(s2) // [1 2 4 5] 注意此时s2的 len=4 cap=5
//取首尾
s3 := s1[0]
s4 := s1[len(s1)-1] // 1
fmt.Print(s3, s4) // 5
}
Map
它是一个无序的key/value对的集合,其中所有的key都是不同的。
var ma map[int]int
ma[1] = 1
fmt.Print(ma)
// 上面方式会报错 panic: assignment to entry in nil map 赋值之前要先make
ma = make(map[int]int, 10)
package main
import "fmt"
func main() {
m0 := map[string]int{
"A": 1,
"B": 2,
"C": 3,
}
m1 := make(map[string]int) // empty map
var m2 map[string]string // nil
fmt.Print(m0, m1, m2)
// map[1:A 2:B 3:C] map[] map[]
var more = map[string]map[string]string{
"k1": {
"a": "A",
"b": "B",
},
}
more["k1"] = val1
fmt.Print(more)
for k, v := range m0 {
fmt.Print(k, v)
}
// A1B2C3
one := m0["A"]
fmt.Print(one) // 1
// 查找不存在的key
one_err := m0["AAA"]
fmt.Print(one_err) // 0
//删除key 删除一个不存在的key,不会报错
delete(m0, "A")
fmt.Print(m0) // map[B:2 C:3]
//判断key是否存在
is_exists, ok := m0["B"]
fmt.Print(is_exists, ok) // 2 true
is_exists0, ok0 := m0["A"]
fmt.Print(is_exists0, ok0) // 0 false
}
结构体
结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。用结构体的经典案例是处理公司的员工信息,每个员工信息包含一个唯一的员工编号、员工的名字、家庭住址、出生日期、工作岗位、薪资、上级领导等等。所有的这些信息都需要绑定到一个实体中,可以作为一个整体单元被复制,作为函数的参数或返回值,或者是被存储到数组中,等等。
package main
import "fmt"
func main() {
// 相当于定义了一个书class
type book struct {
height, width int // 这本书的宽度和高度
page int // 这个本书有多少页
name string // 书名
}
var dpcq book //实例化一本书 斗破苍穹
dpcq.height = 100
dpcq.width = 200
dpcq.page = 10000
dpcq.name = "斗破苍穹"
fmt.Println(dpcq) // {100 200 10000 斗破苍穹}
// 另一种赋值方式
var dzz = book{200, 300, 20000, "大主宰"} // 实例化一本书 大主宰
fmt.Println(dzz) // {200 300 20000 大主宰}
// // 指针访问
n_dzz_name := &dzz.name
*n_dzz_name = *n_dzz_name + " 番外"
fmt.Println(dzz) // {200 300 20000 大主宰 番外
dpcq.name = dpcq.name + " 番外"
fmt.Println(dpcq) // {100 200 10000 斗破苍穹 番外}
var dzz2 *book = &dzz // 指针赋值
dzz2.name = dzz.name + "2"
fmt.Println(dzz2) // &{200 300 20000 大主宰 番外2}
fmt.Println(dzz) // {200 300 20000 大主宰 番外2}
}
方法
相当于在类中定义一个方法
package main
import "fmt"
type cat struct {
Name string
Age int
}
// 这是一个cat的方法
func (c cat) eat() {
fmt.Print(c.Name + "在吃饭") //小猫在吃饭
}
func (cc cat) look() {
cc.Name = "小小毛"
fmt.Print(cc.Name + "在吃饭") //小小毛在吃饭
}
// 定义一个统计方法 count方法能传入一个参数 返回一个数值
func (cc cat) count(age int) int {
return cc.Age + age
}
func main() {
var ccc cat
ccc.Name = "小猫"
ccc.eat()
ccc.look()
fmt.Print(ccc.Name) // 小猫 look方法中的Name不会影响
ccc.Age = 111
add_age := ccc.count(666)
fmt.Print(add_age) // 777
}
封装
test/change/tempconv.go
package change
// 这里有一个 dog类,但是是private的,实现外部调用
type dog struct {
Name string
age int //此处的属性也是private的
}
func NewDog(name string, age int) *dog {
return &dog{
Name: name,
age: age,
}
}
func (d dog) GetAge() int {
return d.age
}
func (d *dog) SetAge(age int) {
d.age = age
}
test/main.go
package main
import (
"fmt"
"test/change"
)
func main() {
//dog.Name = "灰灰"
//会找不到
var dog = change.NewDog("灰灰", 666)
fmt.Print(dog.Name) // 灰灰
fmt.Print(dog.GetAge()) // 666
dog.SetAge(777)
fmt.Print(dog.GetAge()) // 777
}
继承
- 结构体可以使用嵌套结构体的所有熟悉和方法,不区分大小写
- 当结构体和匿名结构体有相同字段或方法时,采用
就近原则,如果希望访问结构体的属性或方法,可以通过指定的方式
package main
import (
"fmt"
)
type info struct {
class string
count int
address string
}
// 添加信息
func (info *info) add(class string, count int, address string) {
info.class = class
info.count = count
info.address = address
}
// 查询信息
func (info info) search() {
fmt.Print("开始查询信息")
fmt.Print("\n")
fmt.Printf("名称=%v 人数=%v 地址=%v", info.class, info.count, info.address)
fmt.Print("\n")
}
func (info info) test() {
fmt.Print("我是基类的")
}
type school struct {
info //匿名结构体
slogan string //自定义熟悉
}
func (school *school) add(class string, count int, address string, slogan string) {
school.class = class
school.count = count
school.address = address
school.slogan = slogan
}
// 查询信息
func (school school) search() {
fmt.Print("开始查询信息")
fmt.Print("\n")
fmt.Printf("名称=%v 人数=%v 地址=%v 口号=%v", school.class, school.count, school.address, school.slogan)
fmt.Print("\n")
}
// 学校特有方法
func (sc school) run() {
fmt.Printf("晨跑的口号是%v", sc.slogan)
fmt.Print("\n")
}
func (school school) test() {
fmt.Print("我是学校的")
}
type company struct {
info //匿名结构体
}
// 公司特有方法
func (sc school) check() {
fmt.Print("打卡")
fmt.Print("\n")
}
func main() {
var info info
info.add("一年级1班", 10, "1001")
info.search()
var sc school
sc.add("哈工大", 100, "东北", "还没想好")
sc.slogan = "还没想好"
sc.search()
sc.info.test()
}
package main
type A struct {
Name string
age int
}
type B struct {
Name string
source string
}
type C struct {
A
B
}
type D struct {
da A
}
func main() {
// var c C
// c.A.Name 相同熟悉时,指定匿名结构体名,除非 C本身有Name
// var d D
// d.da.Name 取名后应该这样访问
}
接口
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type phone struct {
}
func (p phone) Start() {
fmt.Print("手机开始工作...")
}
func (p phone) Stop() {
fmt.Print("手机停止工作...")
}
type camera struct {
}
func (c camera) Start() {
fmt.Print("相机开始工作...")
}
func (c camera) Stop() {
fmt.Print("相机停止工作...")
}
type computer struct {
}
func (cp computer) Work(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
computer := computer{}
phone := phone{}
// camera := camera{}
computer.Work(phone)
// computer.Work(camera)
}
流程控制
if...else
package main
import "fmt"
func main() {
var source = 99
if source <= 60 {
fmt.Println("不及格")
} else if source <= 90 {
fmt.Println("良好")
} else if source <= 100 {
fmt.Println("真棒")
}
// 真棒
}
package main
import "fmt"
func main() {
// err变量的作用域只存在于 if
if err := ErrCode(22); err != 1 {
fmt.Println(err)
// 23
}
}
func ErrCode(info int) int {
return info + 1
}
switch
switch 语句执行的过程从上至下,直到找到匹配项。switch 默认情况下 case 到匹配项后,自带 break 语句(所以不需要显式地写 break),匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用fallthrough。
-
case后面是一个表达式:变量、常量、有返回值的函数都可以
-
case表达式的类型必须和switch的类型一样
-
case后面可以跟多个表达式,用
,隔开
package main
import "fmt"
func main() {
var source int = 100
switch {
case source >= 90:
fmt.Println(90)
case source >= 95:
fmt.Println(95)
case source >= 100:
fmt.Println(100)
}
// 90
// case多个
switch {
case source > 1, source < 50:
fmt.Println("这样也可以哦")
case source >= 100:
fmt.Println(100)
}
switch {
case source >= 90:
fmt.Println(90)
fallthrough
case source >= 95:
fmt.Println(95)
fallthrough
case source >= 100:
fmt.Println(100)
}
// 90
// 95
// 100
}
循环语句
for
package main
import "fmt"
func main() {
var sum = 1
for i := 1; i <= 100; i++ {
sum += i
}
fmt.Println(sum)
// 5050
var count = 1
for count <= 100 {
count++
}
fmt.Println(count)
// 101
// 死循环 一般配合break
var s int = 1
for {
if s <= 100 {
fmt.Println("今天就到这吧")
break
}
s++
}
}
package main
import "fmt"
func main() {
count := 0
sum := 0
for i := 1; i <= 100; i++ {
if (i % 9) == 0 {
count++
sum += i
}
}
fmt.Print(count, sum)
fmt.Print("\n")
number := 6
for i := 0; i <= number; i++ {
fmt.Printf("%d + %d = %d\n", i, number-i, number)
}
// 金字塔
count = 7
for i := 1; i <= count; i++ {
for j := 1; j <= count-i; j++ {
fmt.Print(" ")
}
for x := 1; x <= i*2-1; x++ {
fmt.Print("*")
}
fmt.Print("\n")
}
fmt.Print("\n")
// 空心金字塔
count = 7
for i := 1; i <= count; i++ {
for j := 1; j <= count-i; j++ {
fmt.Print(" ")
}
for x := 1; x <= i*2-1; x++ {
if x == 1 || x == i*2-1 || i == count {
fmt.Print("*")
} else {
fmt.Print(" ")
}
}
fmt.Print("\n")
}
fmt.Print("\n")
//九九乘法表
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%d\t", j, i, i*j)
}
fmt.Print("\n")
}
}
break
package main
func main() {
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if j == 2 {
break
}
}
}
//通过标签跳出 和上面相同
for i := 0; i < 5; i++ {
tag1:
for j := 0; j < 5; j++ {
if j == 2 {
break tag1
}
}
}
tag11:
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if j == 2 {
break tag11
}
}
}
}
continue
跳出本次循环
package main
func main() {
for i := 0; i < 13; i++ {
if i == 10 {
continue
}
}
}
函数
Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
package main
import (
"fmt"
)
func number(a, b int, option string) int {
switch option {
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
return a / b
default:
return 0
}
}
func more(a, b int) (k, y int) {
return a / b, a % b
}
func main() {
fmt.Println(number(1, 2, "+")) // 3
fmt.Println(more(5, 2)) //2 1
w, _ := more(5, 2)
_, z := more(5, 2)
fmt.Println(w) //2
fmt.Println(z) //1
}
func add(x int, y int) int {return x + y}
func sub(x, y int) (z int) { z = x - y; return}
func first(x int, _ int) int { return x }
func zero(int, int) int { return 0 }
闭包
package main
import "fmt"
func main() {
x := 10
increment := func() int {
x++
return x
}
fmt.Println(increment()) // 输出: 11
fmt.Println(increment()) // 输出: 12
fmt.Println(x) // 输出: 10
// 即使 increment 函数被调用,x 的值仍然保持在 main 函数的作用域内。
}
字符串函数
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
str := "abc"
fmt.Print(len(str)) // 3
str = "abc啊"
fmt.Print(len(str)) // 6
//处理中文
str_zh := []rune(str)
fmt.Print(len(str_zh)) //4
// 字符串转int
str_number := "123"
str2int, str2int_err := strconv.Atoi(str_number)
fmt.Print(str2int, str2int_err) // 123 <nil>
// int转字符串
int_number := 123
fmt.Print(strconv.Itoa(int_number)) // 123
//查找一个字符串是否存在于另一个字符串
exists_str := strings.Contains("abcd", "e")
fmt.Print(exists_str) // false
//查找一个字符串在另一个字符串的出现的数量
count_str := strings.Count("abcdabcda", "a")
fmt.Print(count_str) // 3
//比较两个字符串是否相同 不区分大小写
diff_str := strings.EqualFold("abc", "ABC")
fmt.Print(diff_str) // true
// 查找一个字符串在另一个字符串出现的位置,如果没有则是 -1
index_str := strings.Index("abcabc", "b")
fmt.Print(index_str) // 1
index_str = strings.Index("abcabc", "bc")
fmt.Print(index_str) // 1
// 查找一个字符串在另一个字符串最后出现的位置
last_index_str := strings.LastIndex("abcabc", "b")
fmt.Print(last_index_str) // 4
last_index_str = strings.LastIndex("abcabc", "bc")
fmt.Print(last_index_str) // 4
// 替换字符串 replace的第三个参数表示替换次数
replace_str := strings.Replace("abcdabcd", "a", "A", -1)
fmt.Print(replace_str) // AbcdAbcd
replace_str = strings.Replace("abcdabcd", "a", "A", 1)
fmt.Print(replace_str) // Abcdabcd
// 字符串转数组
arr_str := strings.Split("ab,cd", ",")
fmt.Print(arr_str) // [ab cd]
// 字符串转大小写
to_str := strings.ToLower("ABC")
fmt.Print(to_str) // abc
to_str = strings.ToUpper(to_str)
fmt.Print(to_str) // ABC
//去除两边空格
space_str := strings.TrimSpace(" abc ")
fmt.Print(space_str) // abc
//去除左右边指定字符
left_str := strings.TrimLeft("!abc!", "!")
fmt.Print(left_str) // abc!
right_str := strings.TrimRight("!abc!", "!")
fmt.Print(right_str) // !abc
//判断字符串是否以另一个字符串开头
start_str := strings.HasPrefix("https://juejin.cn/user/460064855429152", "https:")
fmt.Print(start_str) // true
//判断字符串是否以另一个字符串结束
stop_str := strings.HasSuffix("https://juejin.cn/user/460064855429152", "9152")
fmt.Print(stop_str) // true
}
时间或日期函数
需要导入
time包
package main
import (
"fmt"
"time"
)
func main() {
// 获取当前时间
now := time.Now()
//fmt.Print(now) // 2024-05-17 20:00:04.3221252 +0800 CST m=+0.017045301
fmt.Print(now.Year()) // 2024
fmt.Print(int(now.Month())) // 5
fmt.Print(now.Day()) // 17
fmt.Print(now.Hour()) // 20
fmt.Print(now.Minute()) // 3
fmt.Print(now.Second()) // 15
// 格式化日期
curr_time := now.Format("2006/01/02 15:04:05")
fmt.Print(curr_time)
// curr_time = now.Format("2006-01-02 15:04:05")
curr_year := now.Format("2006")
fmt.Print(curr_year)
fmt.Print("\n")
start_time := time.Now().Unix()
fmt.Print(start_time)
}
错误处理机制
package main
import "fmt"
func TestErr() int {
defer func() {
err := recover()
if err != nil {
fmt.Print("原来出错了")
}
}()
a := 10
b := 0
return a / b
}
func main() {
TestErr()
fmt.Print("能不能执行啊")
}
goroutine
package main
import (
"fmt"
"time"
)
func together() {
for i := 1; i <= 10; i++ {
fmt.Printf("slave 的循环 %v \n", i)
time.Sleep(time.Millisecond)
}
}
func main() {
go together()
for i := 1; i <= 10; i++ {
fmt.Printf("master 的循环 %v \n", i)
time.Sleep(time.Millisecond)
}
}
channel
package main
import (
"fmt"
)
func main() {
//声明一个管道
var intChan chan int
intChan = make(chan int, 5)
//向管道写数据
w := 10
intChan <- w
intChan <- 66
fmt.Printf("管道的长度= %v 容量= %v", len(intChan), cap(intChan)) // 管道的长度= 2 容量= 5
//向管道读数据
ww := <-intChan
fmt.Print(ww) // 10
fmt.Printf("管道的长度= %v 容量= %v", len(intChan), cap(intChan)) // 管道的长度= 1 容量= 5
}