Golang
本文章中记录一些 Golang 中的语言特性,包括 init 函数,面向对象,defer 关键字,并发编程等。
init 函数与导包
init函数的执行流程是早于main函数的,如果想在main函数执行前做一些事情,可以在init函数中去完成
1 2 3 4 5 6 7 8 9 import ( "fmt" , . "html/template" , _ "text/template" , myhttp "net/http" )
指针 Golang 的指针和 C 语言类型,类型指针不能进行偏移和运算,只需要记住两个符号:&
(取地址)和*
(根据地址取值)
示例如下:
1 2 3 4 5 6 7 8 var var -name *var -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 31 package mainimport "fmt" func swap (pa *int , pb *int ) { var temp int temp = *pa *pa = *pb *pb = temp }func main () { var a int = 10 var b int = 20 var ia *int ia = &a fmt.Printf("a = %v\n" , a) fmt.Printf("&a = %v\n" , &a) fmt.Printf("ia = %v\n" , ia) fmt.Printf("*ia = %v\n" , *ia) fmt.Println("----------------------------------" ) fmt.Println("a = " , a, "b = " , b) swap(&a, &b) fmt.Println("a = " , a, "b = " , b) }
defer 执行顺序
表示一个函数在执行之后,结束之前执行的时机。
golang 函数 函数在go语言中是一级公民,所有的功能单元都定义在函数中,可以重复使用,函数包含函数的名称。参数列表和返回值类型,共同构成函数的签名(signature)
go语言 函数特性
普通函数,匿名函数(没有名称的函数),方法(定义在struct上的函数)
不允许函数重载,不允许函数同名
函数不能嵌套函数,但可以嵌套匿名函数
函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数
函数可以作为参数传递给另一个函数
函数的返回值可以是一个函数
函数参数可以没有名称
函数在传递参数的时候,会先拷贝参数的副本,再将副本传递给函数
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 func test () (name string, age int )} { name = yuluo age = 20 return } func f3 (args... int ) { for _, v := range args { fmt.Println("v := %d " , v) } } func sum (a int , b int ) int { return a + b } func max (a, b int ) int { if a > b { return a } return b } func main () { f3(1 , 2 , 3 , 4 , 7 , 8 ) f3(1 , 3 , 4 ) type f1 func (int , int ) int var ff f1 ff = sum ff (1 , 2 ) ff = max ff = max(1 , 2 ) }
golang 高阶函数 一个函数作为函数的参数,传递给另外一个函数,也可以作为一个函数的返回值返回
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 mainfunc sayHello (name string ) { fmt.Println("Hello, %s" + name) }func f1 (name string , f func (string ) ) { f(name) }func add (a, b int ) int { return a + b }func sub (a, b int ) int { return a - b }func operation (op string ) func (int , int ) int { switch op { case "+" : return add case "-" : return sub default : return nil } }func main () { f1("yuluo" , sayHello) ff := operation("+" ) r := ff(1 , 2 ) fmt.Println(r) ff := operation("-" ) r := ff(1 , 2 ) fmt.Println(r) }
golang匿名函数 golang函数不能嵌套,但是可以通过在函数内部定义匿名函数,实现简单功能的调用
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 语法:func (参数列表) (返回值) 也可以没有参数和返回值func main () { max := func (a, b int ) int { if a > b { return a } return b } r := max(1 , 2 ) fmt.Println(r) r = func (a, b int ) int { if a > b { return a } return b }(1 , 2 ) fmt.Println(r) }
golang 闭包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func add () func (y int ) int { var x int return func (y int ) int { return x += y } }func main () { var f = add() fmt.Println(f(10 )) fmt.Println(f(20 )) fmt.Println(f(30 )) }
OOP 面向对象 类型定义和别名
类型定义仅仅只在代码中存在,在编译完成之后不会存在类型别名
1 2 3 4 5 6 7 8 9 package mainfunc main () { type MyInt int var i MyInt i = 00 }
结构体
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 package mainimport "fmt" type Book struct { title string auth string price int }func changeBook (book Book) { book.title = "huakai" }func changeBookByRefer (book *Book) { book.title = "huakai" }func main () { book := Book{ "java入门" , "yuluo" , 20 , } fmt.Println("原本值:" , book) changeBook(book) fmt.Println("更改之后的值:" , book) changeBookByRefer(&book) fmt.Println("传引用更改之后的值:" , book) var tom struct { id, age int name string } tom.id = 102 tom.age = 20 tom.name = "tom" fmt.Printf("%v\n" , tom) }
方法
面向对象中存在类方法的概念,在 go 中没有 go 方法的概念,但是我们可以声明一些方法属于某个结构体
go 语言的方法是一种特殊的函数,定义于 struct 之上(与 struct 关联,绑定),被称为 struct 的接收者(receiver)通俗理解就是:方法是有接收者的函数
1 2 3 4 5 type mytype struct {}func (recevicer mytype) mymethod(para) return_type {}func (recevicer *mytype) mymethod(para) return_type {}
接口
是一种新的类型定义,把所有具有共性的方法定义在一起,任何其他类型只要是实现了这些方法就是实现了这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type interfae_name interface { method_name1 [return_type] method_name2 [return_type] method_name3 [return_type] }type struct_name struct { }func (struct_name_var struct_name) method_name1() (return_type) { }func (struct_name_var struct_name) method_name2() (return_type) { }
例如 手机和电脑 分别实现 USB 接口,实现读和写功能
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 type USB interface { read() write() }type Computer struct { name string }type Mobile struct { name string }func (c Computer) read() { fmt.Printf("c.name = %v %s\n" , c.name, "电脑使用usb接口读取" ) }func (c Computer) write() { fmt.Printf("c.name = %v %s\n" , c.name, "电脑使用usb接口写" ) }func (m Mobile) read() { fmt.Printf("m.name = %v %s\n" , m.name, "手机使用usb接口读取" ) }func (m Mobile) write() { fmt.Printf("m.name = %v %s\n" , m.name, "手机使用usb接口写" ) }func main () { c := Computer { name: "Dell" , } c.read() c.write() m := Mobile { model: "5G" , } m.read() m.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 34 35 36 37 38 39 40 41 42 43 44 package mainimport "fmt" type Book struct { Title string Auth string Price int }func (this Book) show() { fmt.Println("使用show方法输出的 Book 对象" , this) }func (this *Book) GetAuth() string { return this.Auth }func (this *Book) SetAuth(newName string ) { this.Auth = newName }func main () { book := Book{Title: "java入门" , Auth: "yuluo" , Price: 100 } fmt.Println("原本值:" , book) auth := book.GetAuth() fmt.Println("使用get方法" , auth) book.SetAuth("huakai" ) fmt.Println("调用set方法之后的值:" , book) book.show() }
面向对象继承
通过结构体的嵌套来实现继承
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" type Human struct { Sex string name string }func (this *Human) Eat() { fmt.Println("Human.Eat()..." ) }func (this *Human) Walk() { fmt.Println("Human.walk()..." ) }type Teacher struct { Human work string }func (this *Teacher) Eat() { fmt.Println("Teacher.Eat()..." ) }func (this *Teacher) Teach() { fmt.Println("Teacher.Teach()..." ) }func main () { h := Human{"男" , "lisi" } h.Eat() h.Walk() t := Teacher{Human{"男" , "yuluo" }, "程序员" } t.Eat() t.Teach() t.Walk() }
面向对象多态
多态的基本要素:
有一个父类(接口)
有子类(实现了父类接口中的全部方法)
父类类型的变量(指针)指向子类的具体数据变量
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 package mainimport "fmt" type Animal interface { Sleep() GetColor() string GetType() string }type Cat struct { color string }func (this *Cat) Sleep() { fmt.Println("Cat is sleeping……" ) }func (this *Cat) GetColor() string { return this.color }func (this *Cat) GetType() string { return "Cat" }type Dog struct { color string }func (this *Dog) Sleep() { fmt.Println("Dog is sleeping……" ) }func (this *Dog) GetColor() string { return this.color }func (this *Dog) GetType() string { return "Cat" }func main () { var animal Animal animal = &Cat{"yellow" } animal.Sleep() animal = &Dog{"black" } animal.Sleep() }
空接口含义 interface:通用的万能类型 类似与 java 中的 Object
1 2 map1 := map [string ]interface {}
golang 构造函数 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 Person struct { name string age int }func NewPerson (name string , age int ) (*Person, error ) { if name == "" { return nil , fmt.Errorf("name 不能为空" ) } if age < 0 { return nil , fmt.Errorf("年龄不能小于 0" ) } return &Person{name: name, age: age}, nil }func main () { person, err := NewPeron("yuluo" , 20 ) if err != nil { fmt.Println(err) } fmt.Println(person) }
golang 并发编程 协程
golang 通过关键字 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 func main () { go test4("yuluo" ) test4("huakai" ) }func test4 (msg string ) { for i := 0 ; i < 5 ; i++ { fmt.Printf("msg: %v\n" , msg) time.Sleep(time.Millisecond * 100 ) } }package mainimport ( "fmt" "time" )func newTask () { i := 0 for { i++ fmt.Printf("new Groutine : i = %d\n" , i) } }func main () { go newTask() i := 0 for { i++ fmt.Printf("main goroutine : i = %d\n" , i) time.Sleep(time.Duration(i)) } }
channel
用于携程(goroutine)之间进行通信
创建channel语法
channel的有缓冲和无缓冲问题
当channel已经满了,在向里面写值,就会阻塞
如果channel为空,从里面取值,也会阻塞
1 2 3 unbuffered := make (chan int ) buffered := make (chan int , 10 )
协程发送数据到通道:
1 2 goroutine1 := make (chan string , 5 ) goroutine <- "hello"
从通道接受值:
<- 在通道左边表示接受值,在通道右边表示发送值
同一个通道,发送和接受操作是互斥的;
发送和接受操做在完全完成之前会被阻塞;
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 func main () { go func () { defer fmt.Println("A.defer" ) func () { defer fmt.Println("B.defer" ) runtime.Goexit() fmt.Println("B" ) }() fmt.Println("A" ) }() go func (a int , b int ) { fmt.Println(a, b) }(1 , 2 ) for { time.Sleep(1 * time.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 25 package mainimport "fmt" func main () { c := make (chan int ) go func () { defer fmt.Println("go routine 结束" ) fmt.Println("go routine 正在运行……" ) c <- 666 }() num := <-c fmt.Println("channel中的值:" , num) fmt.Println("main routine 结束" ) }
如果chann中没有值的话,线程会一直阻塞,直到有值之后,在拿出来值。保证了 go routine 的执行顺序
channe l和 range 混合使用(channel 遍历)
1 2 3 for data := range c { fmt.Println(data) }
channer和select混合使用
单流程下一个go只能监控一个channel的执行状态,select可以完成监控多个channel的运行状态
1 2 3 4 5 6 7 8 select { case <- chan1: case chan2 <- 1 : default : }
WaitGroup 同步 线程之间互相等待时使用,A线程没有执行完成,使用此关键字通知B线程等A线程一会
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main () { for i := 0 ; i < 10 ; i++ { go showMsg(6 ) } fmt.Println("end..." ) }func showMsg (i int ) { fmt.Println(i) }
运行如上代码时,主携程并不会等待10个协程执行完毕后在结束,而是自己结束。加入 WairGroup 就可以在10个协程执行完成之后再结束主协程
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" "sync" )var wg sync.WaitGroupfunc main () { for i := 0 ; i < 10 ; i++ { go showMsg(i) wg.Add(1 ) } wg.Wait() fmt.Println("end..." ) }func showMsg (i int ) { defer wg.Done() fmt.Println(i) }
go mod 常用命令
命令
作用
go mod int
生成go.mod文件
go mod download
下载go.mod文件中指明的所有依赖
go mod tidy
整理现有的依赖
go mod graph
查看现有的依赖结构
go mod edit
编辑go.mod文件
go mod vendor
导出项目所有的依赖到vendor目录
go mod verify
检验一个模块是否被篡改过
go mod why
查看为什么需要依赖某模块
golang proxy 选项 这个环境变量主要是用户设置Go模块代理,作用是用于使GO在后续拉取模块版本时直接通过镜像站点快速拉取。
默认值是:https://proxy.golang.org,direct
默认的代理国内访问不了,需要设置国内的代理
direct: 如果在改镜像找不到这个包,会去原本的源拉取