Go语言基础 一些基础语法先不记了,只记录一些必要的东西
变量声明赋值 4种赋值方式
1 2 3 4 var a int var b int = 10 var c = 10 d := 10
最简易、最常用、且在函数体中 -> 使用 :=
1 2 3 4 func main () { a := 100 fmt.Printf("a = %d\n" , a) }
若设置全局变量,则使用除:=
外的3种。
多变量同时赋值
1 2 3 4 5 6 7 var a, b int = 10 , 20 var c, d = 30 , "kagty1" var ( e int = 40 f bool = true )
常量 const
定义常量 -> 只读不能改
1 2 3 4 func main () { const a = 10 fmt.Println(a) }
const
定义枚举类型
1 2 3 4 5 const ( beijing = 1 shanghai = 2 shenzhen = 3 )
iota
-> 只能配合 const()
进行使用,iota
会自动进行累加
1 2 3 4 5 const ( beijing = iota shanghai shenzhen )
函数 1 2 3 4 func demo1 (a string , b int ) int {}func demo1 (a string , b int ) (int , int ) {} func demo1 (a string , b int ) (r1 int , r2 int ) {} func demo1 (a string , b int ) (r1, r2 int ) {}
导包 先执行导入包中的init
函数
目录架构
1 2 3 4 5 Go_Project /lib --lib1 .go --lib2 .go main .go
/lib/lib1.go
1 2 3 4 5 6 7 func init () { fmt.Printf("lib1_init\n" ) } func Lib1_test () { fmt.Printf("lib1_test\n" ) }
/lib/lib2.go
1 2 3 4 5 6 7 func init () { fmt.Printf("lib2_init\n" ) } func Lib2_test () { fmt.Printf("lib2_test\n" ) }
main.go
1 2 3 4 5 6 import "Go_Project/lib" func main () { lib.Lib1_test() lib.Lib2_test() }
Lib1_test
和Lib2_test
函数名的首字母大写代表着这个函数对外共享
匿名导包 我们知道,如果导入了某个包,但是不使用其中的方法,Go
语言会报错,使用 _ "xxxx"
可以避免,即使不显示调用包中的函数方法,也会去调用包中的init
方法
1 2 3 4 5 6 import "Go_Project/lib" func main () { lib.Lib1_test() lib.Lib2_test() }
别名导包 fmt
别名aaa
,使用aaa.Printf
进行打印输出
1 2 3 4 5 6 7 import ( aaa "fmt" ) func main () { aaa.Printf("kagty1\n" ) }
直接调用 使用. "包名"
就可以直接调用Printf
而不用写fmt.Printf
1 2 3 4 5 6 7 import ( . "fmt" ) func main () { Printf("ka\n" ) }
指针 1 2 3 4 5 6 7 8 9 10 func main () { var a int = 10 b := &a fmt.Println(b) fmt.Printf("%d\n" , *b) } 0x140000a2008 10
defer关键字 若使用defer
关键字修饰,则在一个函数中最后执行
函数执行之后出栈执行,在一个函数中,return
的调用在defer
之前
数组 1 2 3 4 5 6 var array1 [10 ]int array2 [10 ]int := {1 ,2 ,3 ,4 } array3 := []int {1 ,2 ,3 ,4 }
slice切片 1、slice
的声明
1 2 3 4 5 6 7 8 9 10 slice1 := []int {1 , 2 , 3 } var slice2 []int slice2 = make ([]int , 3 ) var slice3 []int = make (int [], 3 )slice4 := make ([]int 3 )
2、slice
容量追加与截取
1 2 var slice4 = make ([]int , 3 , 5 ) slice4 = append (slice4, 1 )
切片的截取
1 2 3 4 slice5 := []int {1 ,2 ,3 } num := slice5[0 :2 ] num := slice5[:2 ] num := slice5[2 :]
map 1、map
的声明
1 2 3 4 5 6 var map1 map [string ]string map1 = make (map [string ]string , 10 ) map1["one" ] = "golang" map2 := map [string ]string {"one" : "golang" }
2、map
的使用方式
1 2 3 4 5 6 map3 := make (map [string ]string ) map3["China" ] = "Shanghai" map3["Japan" ] = "Tokyo" delete (map3, "China" )
struct结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type book struct { title string auth string } func main () { var book1 book book1.title = "Golang" book1.auth = "kagty1" fmt.Printf("book1: %v\n" , book1) } ------------------------------------ func changeBook (book *Book) { book.auth = "zhangsan" } func main () { changeBook(&book1) }
面向对象 类名/属性的首字母大写 -> public
首字母小写 -> private
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 type Hero struct { Name string Age string } func (this Hero) Show() { fmt.Printf("%s is %s!\n" , this.Name, this.Age) } func (this *Hero) SetName(newName string ) { this.Name = newName } func (this *Hero) SetAge(newAge string ) { this.Age = newAge } func main () { var hero Hero hero.Name = "kagty1" hero.Age = "18" hero.Show() hero.SetName("kagty2" ) hero.SetAge("20" ) hero.Show() }
创建子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type Hero struct { Name string Age string } func (this Hero) Show() { fmt.Printf("%s is %s!\n" , this.Name, this.Age) } func (this *Hero) SetName(newName string ) { this.Name = newName } func (this *Hero) SetAge(newAge string ) { this.Age = newAge } func main () { s := Hero{"kagty3" , "22" } s.Show() }
Interface 接口作用
1、多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 type Animal interface { Sound() string } type Dog struct {}func (d Dog) Sound() string { return "Woof!" }type Cat struct {}func (c Cat) Sound() string { return "Meow!" }func main () { animals := []Animal{Dog{}, Cat{}} for _, a := range animals { fmt.Println(a.Sound()) } }
2、解耦合
其实感觉这个用法和java
很像,不同的类继承接口去实现接口中的方法
1 2 3 4 5 6 7 8 9 10 11 12 type Logger interface { Log(string ) }type ConsoleLogger struct {}func (c ConsoleLogger) Log(msg string ) { fmt.Println(msg) }type FileLogger struct {}func (f FileLogger) Log(msg string ) { }func main () { var logger Logger = ConsoleLogger{} logger.Log("控制台日志" ) }
3、空接口
空接口不实现任何方法
使用场景如下所示
(1) 某func
需要接收多种类型的参数,为了防止重复构造相同的函数,就引入空接口可以解决
比如想要重写fmt.Println()
,如果想要打印多种类型的数据,可能如下所示重写,但是显得非常冗余
1 2 3 4 5 6 7 8 9 10 11 func Print1 (arg int ) { fmt.Println(arg) } func Print2 (arg string ) { fmt.Println(arg) } func Print3 (arg bool ) { fmt.Println(arg) }
故可以使用空接口,利用了空接口可以承载任意类型的数据的特性
1 2 3 4 5 6 7 8 9 func PrintAny (args interface {}) { for _, arg := range args { fmt.Println("arg = " , arg) } } func main () { PrintAny(1 , "kagty1" , 8.8 ) }
断言 会判断a
的类型是否为string
,如果是string
符合预期,才会进行进一步的逻辑
1 2 3 4 var a interface {} = "kagty1" if str, ok := a.(string ); ok { fmt.Println("str = " , str) }
比如某参数用户可控,为了防止攻击者进行非法攻击,会限制用户输入数据的类型
比如 productId
用户可控,则限制productId
的类型必须为Int
才会执行具体的业务逻辑
反射 使用reflect
库 -> import ("reflect")
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 import ( "fmt" "reflect" ) type User struct { Name string Age int } func (this User) Call() { fmt.Println("user is called" ) } func main () { user := User{"kagty1" , 20 } ReflectDemo(user) } func ReflectDemo (input interface {}) { inputType := reflect.TypeOf(input) fmt.Println("imputType:" , inputType.Name()) inputValue := reflect.ValueOf(input) fmt.Println("imputValue:" , inputValue) for i := 0 ; i < inputType.NumField(); i++ { field := inputType.Field(i) value := inputValue.Field(i).Interface() fmt.Printf("field:%v value:%v\n" , field.Name, value) } }
标签 作用类似于java
中的注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type User struct { Name string `info:"User's name"` Age int `info:"User's age"` } func findTag (str interface {}) { t := reflect.TypeOf(str).Elem() for i := 0 ; i < t.NumField(); i++ { tagString := t.Field(i).Tag.Get("info" ) fmt.Println("info: " , tagString) } } func main () { var user User findTag(&user) }
1 2 3 需要注意的是 `info:"User's name" ` ✅ `info: "User's name" ` ❌ -> 不能出现空格,否则无法正确解析
标签在json中的应用 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 User struct { Name string `json:"name"` Age int `json:"age"` } func findTag (str interface {}) { t := reflect.TypeOf(str).Elem() for i := 0 ; i < t.NumField(); i++ { tagString := t.Field(i).Tag.Get("info" ) fmt.Println("info: " , tagString) } } func main () { user := User{"kagty1" , 20 } jsonStr, err := json.Marshal(user) if err != nil { fmt.Println(err) return } fmt.Println(string (jsonStr)) user2 := User{} err = json.Unmarshal(jsonStr, &user2) if err != nil { fmt.Println(err) } fmt.Println(user2) }
输出
1 2 {"name" : "kagty1" , "age" : 20 } {kagty1 20 }
Goroutine 关键字: go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func task () { i := 0 for { i++ fmt.Println("Goroutine is running: " , i) time.Sleep(1 * time.Second) } } func main () { go task() i := 0 for { i++ fmt.Println("Main is running: " , i) time.Sleep(1 * time.Second) } }
执行结果如下所示,可以看到并行执行的效果:
Channel 通过make
一个channel
,作为中间商,负责两个gorountine
间的通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main () { c := make (chan int ) go func () { defer fmt.Println("Goroutine exited" ) fmt.Println("Goroutine is running" ) c <- 666 }() num := <-c fmt.Println("num is: " , num) }
带有缓存的channel
-> 防止发生阻塞
定义带有缓存的channel
-> make(chan int, 3)
-> 设置缓存大小为3的channel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func main () { c := make (chan int , 3 ) go func () { defer fmt.Println("Goroutine exited" ) for i := 0 ; i < cap (c); i++ { c <- i fmt.Println("Goroutine is running, i is: " , i) } }() time.Sleep(1 * time.Second) for i := 0 ; i < cap (c); i++ { num := <-c fmt.Println("The number is: " , num) } }
执行结果:
1 2 3 4 5 6 7 Goroutine is running , i is : 0 Goroutine is running , i is : 1 Goroutine is running , i is : 2 Goroutine exited The number is : 0 The number is : 1 The number is : 2
在main
线程接受之前可以在缓存中存储,从而避免造成堵塞
关闭channel
-> close(c)
1 2 3 4 5 6 7 for { if data, ok := <-c; ok { fmt.Println(data) } else { break } }
若不关闭channel
,而main
线程一直在获取,在拿到匿名函数子线程赋值的所有值后会发生死锁问题
channel
与range
如上使用for{}
和if
语句过于冗余,使用range
可以更加简洁
1 2 3 for data := range c { fmt.Println(data) }
channel
与select
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 func calc (c, quie chan int ) { x, y := 1 , 1 for { select { case c <- x: x = y y = x + y case <-quit: fmt.Println("End calc.." ) return } } } func main () { c := make (chan int ) quit := make (chan int ) go func () { for i := 0 ; i < 6 ; i++ { fmt.Println(<-c) } quit <- 0 }() calc(c, quit) }
Go Module初始化项目 配置Go
国内代理:https://blog.csdn.net/qq_41168737/article/details/137160590
当使用GoLand
工具创建一个项目后,会自动给我们创建一个go.mod
文件
当我们需要使用,如github
中的某个项目中的某段代码时,如github.com/aceld/zinx
中的代码
则只需要使用go get -u github.com/aceld/zinx
即可
下载成功后就回多出一个go.sum
文件
这之后直接用脱下来的那个项目的代码就可以了
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 import ( "fmt" "github.com/aceld/zinx/ziface" "github.com/aceld/zinx/znet" ) type PingRouter struct { znet.BaseRouter } func (r *PingRouter) Handle(request ziface.IRequest) { fmt.Println("recv from client : msgId=" , request.GetMsgID(), ", data=" , string (request.GetData())) } func main () { s := znet.NewServer() s.AddRouter(1 , &PingRouter{}) s.Serve() }
若运行时依然发生某些错误,可以使用命令 go mod tidy
进行修复,它会自动下载缺失的依赖和未使用的依赖。
若要替换引入依赖版本,执行命令:
1 go mod edit -replace =旧版本=新版本