Hello Go
package main
import "fmt"
func main() {
fmt.Println("hello go")
}
变量的声明
局部变量
// 方法一:声明一个变量 默认值为0
var a int
// 方法二:声明并初始化一个变量
var b int = 10
// 方法三:声明一个变量 自动匹配类型
var c = 20
// 方法四:省去 var 关键字 自动匹配类型
d := 30
// 多变量写法
var xx, yy int = 50, 60
var kk, ll = 70, "hello"
var (
ee int = 10
kk bool = true
vv = "hello"
)
全局变量
// 除了方法四不支持,其他跟局部变量一致
常量的声明
常量
const a int = 10
const (
b = 10
c = 20
)
iota
// 与 const 配合来表示枚举类型
const (
// 每行的 iota 累加1,第一行默认为0
BEIJING = 10 * iota // 0
SHANGHAI // 10
SHENZHEN // 20
)
函数
多返回值
// 返回多个返回值 (匿名)
func hi1(a string, b int) (int, int) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
return 100, 200
}
// 返回多个返回值 (有形参名,默认值为0)
// 形参类型相同可简写为 func hi2(a, b string) (r1, r2 int)
func hi2(a string, b int) (r1 int, r2 int) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
r1 = 100
r2 = 200
return
}
init函数
说明:项目位于 $GOPATH/src/init 中,如果编译报找不到 std lib1/lib2,可以试试用 go mod init 创建Go模块
// main.go
package main
import (
"fmt"
"init/lib1"
"init/lib2"
)
func main() {
lib1.Lib1Test()
lib2.Lib2Test()
fmt.Println(lib1.A)
}
// lib1/lib1.go
package lib1
import "fmt"
var A = "你好A"
func init() {
fmt.Println("lib1 init...")
}
func Lib1Test() {
fmt.Println("lib1 test!")
}
// lib2/lib2.go
package lib2
import "fmt"
func init() {
fmt.Println("lib2 init...")
}
func Lib2Test() {
fmt.Println("lib2 test!")
}
import导包
// 导入匿名包 fmt,不可使用包内方法,但会执行包内的 init() 方法
import _ "fmt"
// 导入别名为 aa 的 fmt 包,可以直接 aa.Println("hi")
import aa "fmt"
// 将 fmt 包内的方法导入当前包,可以直接 Println("hi")
import . "fmt"
指针
package main
import "fmt"
func swap(a *int, b *int) {
temp := *a
*a = *b
*b = temp
}
func main() {
a := 10
b := 20
swap(&a, &b)
fmt.Println("a = ", a, ", b = ", b)
}
defer
// 后接的表达式会在方法最后执行,类似 Java 的 finally
// 存在多个 defer 时,按先进后出的顺序执行 (Stack)
// defer 会在 return 后执行
package main
import "fmt"
func out1() {
fmt.Println("A")
}
func out2() {
fmt.Println("B")
}
func out3() {
fmt.Println("C")
}
func main() {
defer out1()
defer out2()
defer out3()
}
切片slice
数组
package main
import "fmt"
func printArr(arr [3]int) {
// 值传递,printArr::arr 只是 main::arr 的副本
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
arr[0] = 11
}
func main() {
// 数组长度固定
arr := [3]int{1, 2, 3}
// 输出 arr 类型,结果为 [3]int
fmt.Printf("arr type: %T\n", arr)
// 两次输出结果一致
printArr(arr)
printArr(arr)
}
slice (动态数组)
package main
import "fmt"
func printArr(arr []int) {
// 引用传递,printArr::arr 与 main::arr 所指的内存地址相同
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
arr[0] = 11
}
func main() {
// 动态数组
arr := []int{1, 2, 3}
// 输出 arr 类型,结果为 []int
fmt.Printf("arr type: %T\n", arr)
// 第二次输出 arr[0] 为 11
printArr(arr)
printArr(arr)
}
slice
// 声明切片,并完成初始化
slice1 := []int{1, 2, 3}
// 声明切片,但没有分配空间
var slice2 []int
slice2 = make([]int, 3) // 开辟3个空间
// 声明切片,并开辟3个空间
slice3 := make([]int, 3)
// 声明一个切片,分配3个长度,5个空间
numbers := make([]int, 3, 5)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
// 往切片里添加一个元素,长度为4,空间为5
numbers = append(numbers, 1)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
// 往切片里添加两个元素,长度为6,空间为10
// 因为5个空间不够,所以需要扩容,扩容数量为初始空间的数量
numbers = append(numbers, 2, 3)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
// 截取切片
numbers2 := numbers[3:5] // [3, 5)
numbers3 := numbers[:5] // [0, 5)
numbers4 := numbers[3:] // [3, len)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers2), cap(numbers2), numbers2)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers3), cap(numbers3), numbers3)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers4), cap(numbers4), numbers4)
// 复制一个切片
numbers5 := make([]int, 10)
copy(numbers5, numbers)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers5), cap(numbers5), numbers5
Map
package main
import "fmt"
func printMap(myMap map[string]string) {
// map 是引用传递
for key, value := range myMap {
fmt.Printf("key = %s, value = %s\n", key, value)
}
}
func main() {
// ===> 声明一个 map[key]value
// 方式一:
var myMap1 map[string]string
myMap1 = make(map[string]string, 5)
myMap1["one"] = "java"
myMap1["two"] = "c++"
myMap1["three"] = "python"
fmt.Println(myMap1)
// 方式二
myMap2 := make(map[int]string)
myMap2[0] = "java"
myMap2[1] = "c#"
myMap2[2] = "go"
fmt.Println(myMap2)
// 方式三
myMap3 := map[string]string{
"one": "php",
"two": "c#",
"three": "java",
}
// map的使用
printMap(myMap3)
myMap3["four"] = "python" // 添加
myMap3["three"] = "go" // 修改
delete(myMap3, "two") // 删除
printMap(myMap3)
}
struct
封装
package main
import "fmt"
type Book struct {
title string
author string
}
func changeAuthor(book *Book) {
// 结构体默认值传递,需要引用传递需用指针
book.author = "Miss.B"
}
// 方法绑定到 Book 对象,this 默认值传递,引用传递需用指针
func (this *Book) SetAuthor(newAuthor string) {
this.author = newAuthor
}
func main() {
var book1 Book
book1.title = "hello"
book1.author = "Mr.A"
fmt.Println(book1)
changeAuthor(&book1)
fmt.Println(book1)
book1.SetAuthor("Ms.C")
fmt.Println(book1)
}
继承
package main
import "fmt"
type Man struct {
name string
sex int
}
func (this *Man) Eat() {
fmt.Println("Man Eat...")
}
func (this *Man) Walk() {
fmt.Println("Man Walk...")
}
type Superman struct {
Man
level int
}
func (this *Superman) Walk() {
fmt.Println("Superman Walk...")
}
func main() {
man := Man{"Mr.A", 19}
man.Eat()
man.Walk()
superman := Superman{Man{"Mr.B", 18}, 100}
superman.Eat()
superman.Walk()
fmt.Println(man)
fmt.Println(superman)
}
多态
// 基本要素:
// 1. 有一个父类 (有接口)
// 2. 有子类 (实现了父类的全部接口方法)
// 3. 父类指针变量指向子类具体数据
package main
import "fmt"
// 动物 (父类)
type AnimalIF interface {
Sleep()
GetType() string
}
// 猫 (子类)
type Cat struct {
color string
}
func (this *Cat) Sleep() {
fmt.Println("Cat is sleeping...")
}
func (this *Cat) GetType() string {
return "Cat"
}
// 狗 (子类)
type Dog struct {
color string
}
func (this *Dog) Sleep() {
fmt.Println("Dog is sleeping...")
}
func (this *Dog) GetType() string {
return "Dog"
}
func main() {
var animal AnimalIF
// animal = cat
animal = &Cat{"white"}
animal.Sleep()
fmt.Println(animal.GetType())
// animal = dog
animal = &Dog{"Yellow"}
animal.Sleep()
fmt.Println(animal.GetType())
}
万能数据类型 (空接口)
package main
import "fmt"
func show(arg interface{}) {
fmt.Println(arg)
// 类型断言
value, ok := arg.(string)
if ok {
fmt.Println("arg is string, value is ", value)
} else {
fmt.Println("arg is not string")
}
}
func main() {
show(100)
show(3.14)
show("hello")
}
反射
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (this User) Call() {
fmt.Println("user is called !")
}
func main() {
user := User{1, "Alice", 18}
doFieldAndMethod(user)
}
func doFieldAndMethod(input interface{}) {
// 获取 input 类型
inputType := reflect.TypeOf(input)
fmt.Println("inputType: ", inputType)
// 获取 input 数值
inputValue := reflect.ValueOf(input)
fmt.Println("inputValue: ", inputValue)
// 获取 input 里的字段
for i := 0; i < inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 获取 input 里的方法
for i := 0; i < inputType.NumMethod(); i++ {
method := inputType.Method(i)
fmt.Printf("%s: %v\n", method.Name, method.Type)
}
}
标签
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type resume struct {
Name string `json:"name" comment:"姓名"`
Sex string `json:"sex"`
}
func findTag(str interface{}) {
t := reflect.TypeOf(str).Elem()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("%s's comment is: %s\n", field.Name, field.Tag.Get("comment"))
}
}
func main() {
r1 := resume{"小明", "男"}
// 对象转json字符串
jsonStr, err := json.Marshal(r1)
if err != nil {
fmt.Println("json marshal error!")
return
}
fmt.Printf("jsonStr: %s\n", jsonStr)
// json字符串转对象
var r2 resume
json.Unmarshal(jsonStr, &r2)
fmt.Printf("r2: %v\n", r2)
// 查找tag
findTag(&r1)
}
goroutine
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
go func() {
defer fmt.Println("A.defer")
func() {
defer fmt.Println("B.defer")
runtime.Goexit() // 终止当前goroutine
fmt.Println("B")
}()
fmt.Println("A")
}()
// 异步非阻塞 这里没办法接受返回值
go func(a int, b int) bool {
fmt.Printf("a = %d, b = %d\n", a, b)
return true
}(10, 20)
for {
time.Sleep(1 * time.Second)
}
}
channel
无缓冲channel
/*
无缓冲channel说明:
如果goroutine往channel里写数据,但main还没来得及读数据,goroutine会阻塞等待main读取后再继续执行
反之,如果main执行的比较快开始读数据,但goroutine还没来得及往channel里写数据,那么main会阻塞等待goroutine写数据后再继续执行
*/
package main
import "fmt"
func main() {
// 定义一个无缓冲channel
c := make(chan int)
go func() {
defer fmt.Println("goroutine结束...")
fmt.Println("goroutine运行中...")
c <- 123 // 将123发送给c
}()
num := <-c // 从c中取值赋值给num
fmt.Println("num = ", num)
fmt.Println("main结束...")
}
有缓存channel
/*
有缓冲channel说明:
goroutine会持续往channel里写数据,直到channel缓冲区已满就会阻塞
同理,main会持续读数据,直到channel缓冲区已空就会阻塞
*/
package main
import (
"fmt"
"time"
)
func main() {
// 定义一个缓冲区大小为2的channel
c := make(chan int, 2)
fmt.Printf("len(c) = %d, cap(c) = %d\n", len(c), cap(c))
go func() {
defer fmt.Println("goroutine结束...")
for i := 0; i < 4; i++ {
c <- i
fmt.Printf("goroutine运行中, 发送元素%d, len(c) = %d, cap(c) = %d\n", i, len(c), cap(c))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 4; i++ {
num := <-c
fmt.Println("num = ", num)
}
fmt.Println("main结束...")
}
关闭channel
/*
关闭channel说明:
关闭channel后,无法再继续往channel里写数据,但可以读数据
对一个nil channel读写数据都会阻塞
*/
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
// 往c里持续写数据
for i := 0; i < 3; i++ {
c <- i
}
// 关闭c
close(c)
}()
// 死循环获取并输出c,直到c关闭 (c永远不关闭则造成死锁)
for {
// if 初始化语句; 条件语句
if data, ok := <-c; ok {
fmt.Println("data = ", data)
} else {
break
}
}
// 同上效果,但是用range更方便 (上面已经读取完数据了,此时c关闭且为空所以不会输出任何值)
for data := range c {
fmt.Println("data = ", data)
}
}
channel与select
package main
import (
"fmt"
)
func main() {
c := make(chan int)
quit := make(chan bool)
go func() {
// 读取并输出3次数据
for i := 0; i < 3; i++ {
fmt.Println(<-c)
}
// 完成后设置退出
quit <- true
}()
num := 1
for {
select {
case <-quit: // 如果读取quit成功
fmt.Println("quit")
return
case c <- num: // 如果将num写入c成功
num *= 2
/*
default: // 如果以上都不执行
num++
*/
}
}
}









