GoLang Notes
The beginning of GO
KNOW
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println("Hello, World!")
age := 18
//var age int=18
fmt.Println("age=", age)
fmt.Println(unsafe.Sizeof(age))
}
IF ELSE
package main
import (
"fmt"
"math/rand"
)
func main() {
var count = rand.Int()
fmt.Println(count)
if count < 30 {
fmt.Println("Too small")
} else if count == 30 {
fmt.Println("Very good")
} else {
fmt.Println("Too big")
}
}
Switch
package main
import "fmt"
func main() {
var score int = 60
switch score {
case 100:
fmt.Println("Very Good")
case 90:
fmt.Println("Good")
case 80, 70, 60:
fmt.Println("Nice")
default:
fmt.Println("a little bad")
}
}
//fallthrough 使用方法与break等类似 穿透处理 即执行下一层的代码
For
package main
import "fmt"
func main() {
sum := 0
for i := 1; i <= 5; i++ {
sum += i
}
fmt.Println(sum)
}
FUNC
package main
import "fmt"
func add(num1 int, num2 int) int {
return num1 + num2
}
//函数名首字母大写,所有包都可以用
func main() {
num1 := 10
num2 := 20
sum := add(num1, num2)
fmt.Println(sum)
}
/*
package main
import "fmt"
func add(num1 int, num2 int) (int, int) {
return num1 + num2, num2 - num1
}
func main() {
num1 := 10
num2 := 20
sum, _ := add(num1, num2)
fmt.Println(sum)
}
*/
接收多个参数
package main
import "fmt"
//可变参数放在形参列表的最后
func test(args ...int) {
for i := 0; i < len(args); i++ {
fmt.Println(args[i])
}
}
func main() {
test(1, 2, 3, 4, 5)
}
反引号 ``
以字符串原生形式输出,包括换行和特殊字符
基本数据类型转字符串
package main
import "fmt"
func main() {
a := 100
str := fmt.Sprintf("%d", a)
fmt.Printf("str Type %T\nstr=%v", str, str)
var num int64 = 7
str := strconv.FormatInt(num, 2) // strconv.Itoa
fmt.Printf("str=%v %T", str, str)
}
%d......
package main
import "fmt"
func main() {
a := 100
str := fmt.Sprintf("%d", a)
fmt.Printf("str Type %T\nstr=%v", str, str)
/* %v 默认格式
%+v 输出结构体时会添加字段名
%#v 值的go语法表示
%T 值的类型的GO语法表示
%% 百分号
%t bool值
%b 二进制
%c unicode码值
%d 十进制
%o 八进制
%x 16进制 使用a-f
%X 16进制 使用A-F
%U Unicode格式
*/
}
string转数据类型
package main
import (
"fmt"
"strconv"
)
func main() {
var str = "true"
//函数第二个参数是err error
b, _ := strconv.ParseBool(str)
fmt.Printf("%T\n", b)
var str2 = "114514"
var n1 int64
n1, _ = strconv.ParseInt(str2, 10, 0)
fmt.Println(n1)
var str3 = "123.456"
var f1 float64
f1, _ = strconv.ParseFloat(str3, 64)
fmt.Println(f1)
}
指针
package main
import "fmt"
func main() {
a := 10
var p = &a
fmt.Println(*p)
fmt.Printf("a %v , p %v\n", &a, p)
fmt.Printf("%v", &p)
}
值类型和引用类型
值类型:int float bool string struct 和数组 (变量直接存储值,内存通常在栈中分配
引用类型:指针 slice切片 map 管道chan interface (变量存储一个地址,内存通常在堆上分配
接收用户输入
package main
import "fmt"
func main() {
var name string
var age byte
var salary float64
var isPass bool
/*
fmt.Println("请输入姓名 ")
fmt.Scanln(&name)
fmt.Println("请输入年龄 ")
fmt.Scanln(&age)
fmt.Println("请输入薪水 ")
fmt.Scanln(&salary)
fmt.Println("请输入是否通过考试 ")
fmt.Scanln(&isPass)
fmt.Printf("姓名:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v\n", name, age, salary, isPass)
*/
fmt.Println("请输入你的姓名,年龄,薪水,是否通过考试,使用空格隔开")
_, err := fmt.Scanf("%s %d %f %t", &name, &age, &salary, &isPass)
if err != nil {
return
}
fmt.Printf("姓名:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v\n", name, age, salary, isPass)
}
负数补码
符号位不变 取反+1
空心金字塔
package main
import "fmt"
func main() {
var n int
fmt.Scanln(&n)
for i := 0; i < n; i++ {
for j := 0; j <= 2*n-1; j++ {
if j == n-i || j == n+i || (i == n-1 && j >= n-i && j <= n+i) {
fmt.Print("*")
} else {
fmt.Print(" ")
}
}
fmt.Print("\n")
}
}
goto
package main
import (
"fmt"
)
func main() {
fmt.Println("1")
goto label1
fmt.Println("2")
fmt.Println("3")
label1:
fmt.Println("4")
fmt.Println("5")
}
导包
go mod(不同目录下
module main
go 1.20
require (
"ob1" v0.0.0
)
replace (
"ob1" => "../ob1"
)
init
初始化 在main前面先执行
执行顺序:全局变量定义->init->main
package main
import "fmt"
func init() {
fmt.Println("init ...")
}
func main() {
fmt.Println("main ...")
}
/*
init ...
main ...
*/
匿名函数
定义时直接调用(只能调用一次)
package main
import "fmt"
func main() {
res := func(n1 int, n2 int) int {
return n1 + n2
}(10, 10)
fmt.Println(res)
}
匿名函数赋给变量
package main
import "fmt"
func main() {
a := func(n1 int, n2 int) int {
return n1 + n2
}
res := a(10, 10)
fmt.Println(res)
}
全局匿名函数
package main
import "fmt"
var (
Fun1 = func(n1 int, n2 int) int {
return n1 + n2
}
)
func main() {
res := Fun1(10, 10)
fmt.Println(res)
}
闭包
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
package main
import (
"fmt"
)
func AddUpper() func(int) int {
var n = 10
return func(i int) int {
n = n + i
return n
}
}
//返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包 还有就是n被视为常驻内存,但并不是全局变量
func main() {
f := AddUpper()
fmt.Println(f(1)) //11
fmt.Println(f(2)) // 13
fmt.Println(f(3)) //16
}
package main
import (
"fmt"
"strings"
)
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
// suffix和匿名函数构成一个闭包
func main() {
f := makeSuffix(".jpg")
fmt.Println("文件名处理后", f("winter"))
fmt.Println("文件名处理后", f("bird.jpg"))
}
defer(延时机制
why?
函数执行完毕后,及时的释放资源。
案例
package main
import "fmt"
func sum(n1 int, n2 int) int {
defer fmt.Println("ok1 n1=", n1)
defer fmt.Println("ok2,n2", n2)
res := n1 + n2
fmt.Println("ok3 res=", res)
return res
}
//执行到defer时,会将defer后面的语句(此时的变量值也会)压入到独立的栈中(defer栈
//函数执行完毕后从defer栈中,按照先入后出的方式执行(栈顶进出)
func main() {
res := sum(1, 2)
fmt.Println("res=", res)
}
/*
ok3 res= 3
ok2,n2 2
ok1 n1= 1
res= 3
*/
函数变量传递方式
值传递 &&引用传递
传递给函数的都是变量的副本,不同的是 引用传递是地址拷贝,效率高,因为数据量小
len()
str := "hello丁"
fmt.Printf("len str = %v\n", len(str)) //len str = 8
处理字符串遍历时中文问题
str2 := "hello丁"
str2Run := []rune(str2) //转为切片处理中文问题,以便遍历不出现乱码
for i := 0; i < len(str2Run); i++ {
fmt.Printf("字符=%c\n", str2Run[i])
}
strconv
Atoi
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("Atoi_error")
} else {
fmt.Println("转成的结果是", n)
}
Itoa
str := strconv.Itoa(12345)
fmt.Printf("str=%v type=%T", str, str)
10进制转 2,8,16进制
//10进制转 2,8,16进制 返回相应的字符串
str := strconv.FormatInt(123, 2)
fmt.Println("123对应的二进制", str)
[]byte切片
bytes := []byte("hello golang")
fmt.Println("bytes=", bytes)
//bytes= [104 101 108 108 111 32 103 111 108 97 110 103]
[]byte切片转string
str := string([]byte{104, 101, 108, 108, 111, 32, 103, 111, 108, 97, 110, 103})
fmt.Println(str)
//hello golang
strings
查找子串是否在指定的字符串中 Contains
a := strings.Contains("seafood", "foo")
b := strings.Contains("seafood", "f1")
fmt.Println("a=", a)
fmt.Println("b=", b)
//a= true
//b= false
子串在指定字符串中的个数 Count
num := strings.Count("dyyyyyyyyt", "y")
fmt.Println("num=", num) //8
字符串不区分大小写比较 EqualFold
//==区分字符串大小写
b := strings.EqualFold("abc", "AbC")
fmt.Println("b=", b)//b= true
返回子串在字符串第一次出现的index值 Index
//返回子串在字符串第一次出现的index值 如果没有就返回-1
index := strings.Index("NUTR_Hard", "TR")
fmt.Println("index =", index) //index = 2
返回子串在字符串最后一次出现的index值 LastIndex
//返回子串在字符串最后一次出现的index值 如果没有就返回-1
index := strings.LastIndex("NUTR_Hard_NUTR", "TR")
fmt.Println("index =", index) //index = 12
将指定的子串替换成另外一个子串 Replace
//将指定的子串替换成另外一个子串
//n可以指定你希望替换几个 n=-1则表示全部替换
str := strings.Replace("go hello", "go", "go语言", -1)
fmt.Println("str =", str)//str = go语言 hello
按照某个字符分割字符串 Split
//按照某个字符分割字符串
strArr := strings.Split("golang_learning_GO", "_")
fmt.Println(strArr)//[golang learning GO]
字符串大小写转换
//将字符串的字母进行大小写的转换
str := "goLang Hello"
str = strings.ToLower(str)
fmt.Println(str) //golang hello
str = strings.ToUpper(str)
fmt.Println(str)
//golang hello
//GOLANG HELLO
删除字符串两边的空格 TrimSpace
//删除字符串两边的空格
str := strings.TrimSpace(" GOlang ")
fmt.Println(str)
删除字符串两边的指定字符 Trim
//删除字符串两边的指定字符
str := strings.Trim("!GoLang!", "!")
fmt.Println(str)
TrimLeft(改左边) TrimRight(改右边)
用法同上
判断字符串是否以指定字符开头 HasPrefix
//判断字符串是否以指定字符开头
a := strings.HasPrefix("GoLang", "Go")
fmt.Println(a) //True
判断字符串是否以指定字符结尾 HasSuffix
//判断字符串是否以指定字符结尾
a := strings.HasSuffix("GoLang", "Lang")
fmt.Println(a) //True
Time
time.Now
t := time.Now()
fmt.Println(t) //2023-08-20 19:12:54.8102033 +0800 CST m=+0.001799701
fmt.Printf("%T", t) //time.Time
年月日时分秒
t := time.Now()
fmt.Printf("年=%v\n", t.Year())
fmt.Printf("月=%v\n", int(t.Month()))
fmt.Printf("日=%v\n", t.Day())
fmt.Printf("时=%v\n", t.Hour())
fmt.Printf("分=%v\n", t.Minute())
fmt.Printf("秒=%v\n", t.Second())
格式化输出
t := time.Now()
fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d\n", t.Year(), t.Month(), t.Day(), t.Hour(),t.Minute(), t.Second())
//当前年月日 2023-8-20 19:25:25
t := time.Now()
date := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d\n", t.Year(), t.Month(), t.Day(), t.Hour(),t.Minute(), t.Second())
fmt.Println(date)
t := time.Now()
fmt.Printf(t.Format("2006/01/02 15:04:05"))
时间常量
Sleep
package main
import (
"fmt"
"time"
)
func main() {
i := 0
for {
i++
fmt.Println(i)
time.Sleep(time.Second)
if i == 10 {
break
}
}
}
Unix时间戳和UnixNano
Unix: 从 January 1,1970 UTC到时间点所经过的时间(单位s)
UnixNano: 从 January 1,1970 UTC到时间点所经过的时间(单位纳秒)
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
fmt.Println(t.Unix())
fmt.Println(t.UnixNano())
}
//1692532567
//1692532567631470700
内置函数
简介
- len 用来求长度,比如string array slice map channel
- new 用来分配内存,主要用来分配值类型,比如int float32 struct 返回的时指针
- make 用来分配内存 主要用来分配引用类型,比如chan map slice
new
package main
import "fmt"
func main() {
num := new(int)
a := 1
num = &a
fmt.Printf("a的地址=%v\n", &a)
fmt.Printf("num的类型%T , num的值=%v , num的地址=%v",
num, num, &num)
}
//a的地址=0xc00001a0d0
//num的类型*int , num的值=0xc00001a0d0 , num的地址=0xc00000a028
make
var 切片名 []type = make([],len,[cap])
package main
import "fmt"
func main() {
var slice = make([]int, 4)
fmt.Println(slice)
slice[0] = 1
fmt.Println(slice)
}
//[0 0 0 0]
//[1 0 0 0]
错误处理
简介
- 默认情况下,当发生错误后(panic),程序就会退出(崩溃)
- 我们希望,当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行.
示例
package main
import "fmt"
func test() {
defer func() {
err := recover()
if err != nil {
fmt.Println("err=", err)
}
}()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println(res)
}
func main() {
test()
fmt.Println("True")
}
//err= runtime error: integer divide by zero
//True
自定义错误
package main
import (
"errors"
"fmt"
)
func readConf(name string) (err error) {
if name == "config.ihi" {
return nil
} else {
return errors.New("读取文件错误")
}
}
func test() {
err := readConf("1")
if err != nil {
//读取错误 并输出这个错误,然后终止程序
panic(err)
}
fmt.Println("test()继续执行")
}
func main() {
test()
fmt.Println("main()继续执行")
}
//panic: 读取文件错误
//goroutine 1 [running]:
//main.test()
// E:/GOlanguage/main/main.go:19 +0x49
//main.main()
// E:/GOlanguage/main/main.go:25 +0x19
数组
案例
package main
import "fmt"
func main() {
var hens [6]float64
hens[0] = 1.0
hens[1] = 2.0
hens[2] = 3.0
hens[3] = 4.0
hens[4] = 5.0
hens[5] = 5.0
sum := 0.0
for i := 0; i < len(hens); i++ {
sum += hens[i]
}
fmt.Println(sum)
}
初始化数组
var Array01 [3]int = [3]int{1, 2, 3}
var Array02 = [3]int{1, 2, 3}
var Array03 = [...]int{1, 2, 3}
var Array04 = [3]string{0: "d", 1: "y", 2: "t"}
for_range
package main
import "fmt"
func main() {
var heroes = [3]string{"d", "y", "t"}
for i, v := range heroes {
fmt.Printf("i=%v , v=%v\n", i, v)
}
}
数组倒序
package main
import (
"fmt"
"math/rand"
)
func main() {
var num [5]int
for i := 0; i < 5; i++ {
num[i] = rand.Intn(100) //[0,100]
}
fmt.Println(num)
for i := 0; i < len(num)/2; i++ {
num[i], num[len(num)-1-i] = num[len(num)-1-i], num[i]
}
fmt.Println(num)
}
slice
简介
- 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
- 切片的使用与数组累死,遍历切片,访问切片的元素和求切片长度都一样
- 切片长度时可以变化的,因此切片是一个动态变化数组
- var a []int
案例
代码
package main
import "fmt"
func main() {
var intArr = [...]int{1, 22, 33, 66, 99}
slice := intArr[1:3] //[1,3)
fmt.Println(slice)
fmt.Printf("%T\n", slice)
fmt.Printf("%v", cap(slice)) //切片的容量是可以动态变化
}
//[22 33]
//[]int
//4
内存示意图
可以看成一个结构体 有三个变量 ptr len cap
切片遍历
package main
import "fmt"
func main() {
arr := [...]int{10, 20, 30, 40, 50}
slice := arr[1:4]
for i, v := range slice {
fmt.Println(slice[i] == v)
}
}
append
- 本质就是对数组扩容
- go底层会创建一个新的数组newArr
- 将slice原来包含的元素拷贝到新的数组newArr
- slice重新引用到newArr
- newArr是在底层维护的,程序员不可见
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4}
slice = append(slice, 5)
fmt.Println(slice)
slice = append(slice, slice...)
fmt.Println(slice)
}
//[1 2 3 4 5]
//[1 2 3 4 5 1 2 3 4 5]
copy
- 参数数据类型是切片
- 下面代码a和slice空间独立,互不影响,copy是值传递
package main
import "fmt"
func main() {
var a = []int{1, 2, 3, 4, 5}
var slice = make([]int, 10)
fmt.Println(slice)
copy(slice, a)
fmt.Println(slice)
}
//[0 0 0 0 0 0 0 0 0 0]
//[1 2 3 4 5 0 0 0 0 0]
string and slice
- string底层是一个byte数组,因此string也可以进行切片处理
- string类似于切片,包含ptr与len
- string本身不可变
- 如果需要修改 可以将string转成[]byte / []rune
package main
import "fmt"
func main() {
str := "hello@atguigu"
slice := str[6:]
fmt.Println(slice)
}
修改字符串
package main
import "fmt"
func main() {
str := "hello@atguigu" //[]byte是按照字节处理 而中文三个字节
arr := []byte(str) //如果有中文的话 转成[]rune便可以 []rune是按照字符处理
arr[0] = 'z'
str = string(arr)
fmt.Println(str)
}
冒泡排序
package main
import "fmt"
func BubbleSort(arr *[5]int) {
for i := 0; i < len(arr)-1; i++ {
for j := i + 1; j < len(arr)-i; j++ {
if arr[j-1] > arr[j] {
arr[j], arr[j-1] = arr[j-1], arr[j]
}
}
}
}
func main() {
arr := [...]int{24, 69, 80, 57, 13}
BubbleSort(&arr)
fmt.Println(arr)
}
二分法
for循环
func BinaryFind(arr *[6]int, findVal int) {
left := 0
right := len(arr) - 1
for left <= right {
mid := (left + right) >> 1
if arr[mid] > findVal {
right = mid - 1
} else if arr[mid] < findVal {
left = mid + 1
} else if arr[mid] == findVal {
fmt.Println(mid)
return
}
}
fmt.Println("Not Found")
return
}
递归
func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {
if leftIndex > rightIndex {
fmt.Println("Not Found")
return
}
mid := (leftIndex + rightIndex) / 2
if arr[mid] > findVal {
BinaryFind(arr, leftIndex, mid-1, findVal)
} else if arr[mid] < findVal {
BinaryFind(arr, mid+1, rightIndex, findVal)
} else if arr[mid] == findVal {
fmt.Println(mid)
}
}
map
简介
- var 变量名 map[key_type]value_type
- 如果定义的key与map里已存在的相同,则会对value进行覆盖
- 需要用make分配内存
案例
package main
import (
"fmt"
)
func main() {
var a map[string]string
a = make(map[string]string, 10)
a["1"] = "d"
a["2"] = "y"
a["3"] = "t"
fmt.Println(a)
}
//map[1:d 2:y 3:t]
初始化
package main
import "fmt"
func main() {
//第一种
var a map[string]string
a = make(map[string]string, 10)
//第二种
cities := make(map[string]string)
cities["No1"] = "北京"
cities["No2"] = "Tianjing"
fmt.Println(a)
fmt.Println(cities)
//第三种
heroes := map[string]string{
"hero": "d",
"hero2": "yt",
}
fmt.Println(heroes)
}
delete
delete(map,"key")
key不存在的话也不会报错
package main
import "fmt"
func main() {
cities := make(map[string]string)
cities["No1"] = "北京"
cities["No2"] = "天津"
delete(cities, "No2")
fmt.Println(cities)
}
//map[No1:北京]
如果想全部删除
一一遍历或者直接make一个新空间
map查找
package main
import "fmt"
func main() {
cities := make(map[string]string)
cities["No1"] = "北京"
cities["No2"] = "天津"
val, found := cities["No1"]
if found {
fmt.Println(val)
} else {
fmt.Println("False")
}
}
map遍历
package main
import "fmt"
func main() {
cities := make(map[string]string)
cities["No1"] = "北京"
cities["No2"] = "天津"
for key, value := range cities {
fmt.Printf("%v = %v\n", key, value)
}
}
map 切片
package main
import "fmt"
func main() {
var monsters []map[string]string
monsters = make([]map[string]string, 2)
if monsters[0] == nil {
monsters[0] = make(map[string]string, 2)
monsters[0]["name"] = "牛魔王"
monsters[0]["age"] = "500"
}
if monsters[1] == nil {
monsters[1] = make(map[string]string, 2)
monsters[1]["name"] = "玉兔精"
monsters[1]["age"] = "400"
}
//append函数,动态增加
monster := map[string]string{
"name": "新的妖怪",
"age": "200",
}
monsters = append(monsters, monster)
fmt.Println(monsters)
}
细节
- map是引用类型
- map make完以后,想要再增加元素会自动扩容,并不会发生panic
- map的value也经常使用struct类型
package main
import "fmt"
type Stu struct {
Name string
Age int
Address string
}
func main() {
students := make(map[string]Stu, 10)
stu1 := Stu{Name: "Tom", Age: 18, Address: "北京"}
stu2 := Stu{Name: "Xiao_Hong", Age: 18, Address: "北京"}
students["No1"] = stu1
students["No2"] = stu2
fmt.Println(students)
for k, v := range students {
fmt.Println(k, v.Name, v.Age, v.Address)
}
}
Struct
字段
字段是结构体的一部分,一般是基本数据类型,也可以是引用类型。
type Cat struct {
Name string //属性/字段/filed
Age int
Color string
}
案例
package main
import "fmt"
type Person struct {
Name string //属性/字段/filed
Age int
Scores [5]float64
ptr *int
slice []int
map1 map[string]string
}
func main() {
per := Person{}
per.slice = make([]int, 1)
per.slice[0] = 100
fmt.Println(per)
}
内存布局
细节
-
结构体之间的拷贝是值拷贝
-
p := new(Person) //p是一个指针 (*p).Name || p.Name
-
var p *Person = &Person{}
-
结构体字段在内存中连续分布
-
package main import "fmt" type a struct { Num int } type b struct { Num int } func main() { var A = a{1} var B b B = b(A) //要求字段完全一样 (name type) fmt.Println(A) fmt.Println(B) } //{1} //{1}
反射
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Skill string `json:"skill"`
}
func main() {
monster := Monster{"牛魔王", 500, "芭蕉扇"}
jsonMonster, err := json.Marshal(monster)
if err != nil {
fmt.Println("Json处理错误")
}
fmt.Println(string(jsonMonster))
}
//{"name":"牛魔王","age":500,"skill":"芭蕉扇"}
方法
简介
-
在某些情况下,我们需要声明(定义)方法.比如结构体除了需要一些字段外有时候还要有一些行为,这时需要方法才能完成
-
GOlang中的方法是作用在指定的数据类型上的(和指定数据类型绑定),因此自定义类型,都可以由方法,而不仅仅是结构体
-
func(recevier type) methodName(参数列表) (返回值列表){
方法体
return 返回值
}
案例
- test方法和A类型绑定
- test方法只能通过A类型的变量来调用,不能使用其他类型变量来调用
package main
import "fmt"
type A struct {
Name string
}
func (a A) test() {
fmt.Println(a.Name)
}
func main() {
var p = A{Name: "雨"}
p.test()
}
package main
import "fmt"
type A struct {
Name string
}
func (a A) test(n int) {
sum := 0
for i := 1; i <= n; i++ {
sum += i
}
fmt.Println(sum)
}
func main() {
var p = A{Name: "雨"}
p.test(5)
}
String()方法
可以自定义fmt.Println()输出
package main
import "fmt"
type Student struct {
Name string
Age int
}
func (stu *Student) String() string {
str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
return str
}
func main() {
stu := Student{Name: "Tom", Age: 20}
fmt.Println(&stu)
fmt.Println(stu)
}
//Name=[Tom] Age=[20]
//{Tom 20}
方法和函数的区别
-
调用方式不一样
函数的调用方式 函数名(实参列表)
方法的调用方式 变量名.方法名(实参列表)
-
对于普通函数,接收者为值类型,不能将指针类型的数据直接传递,反之亦然
-
对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法(但本质依旧是值拷贝),反过来同样也可以
工厂模式
结构体开头小写,并且其他包想调用
package ob1
type student struct {
Name string
score float64
}
func NewStudent(n string, s float64) *student {
return &student{
Name: n,
score: s,
}
}
func (s *student) Getscore() float64 {
return s.score
}
继承
package main
type Goods struct{
Name string
Price int
}
type Book struct{
Goods
Writer string
}
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
func (stu *Student) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) Setscore(score int) {
stu.Score = score
}
type Pupil struct {
Student
}
func (p *Pupil) testing() {
fmt.Println("小学生正在考试中")
}
func main() {
pupil := &Pupil{}
pupil.Name = "tom~"
pupil.Student.Age = 8
pupil.testing()
pupil.Student.Setscore(70)
pupil.Student.ShowInfo()
}
接口
基本介绍
GoLang中多态特性主要通过接口来体现的
接口是一种抽象类型,用于定义某个对象的行为规范。接口定义了一组方法的签名,任何实现了这些方法的类型都能被称之为该接口的实现类型。接口包括两部分:静态类型和动态类型,其中静态类型是指接口的类型定义,而动态类型是指接口的具体实现类型。
GoLang中的接口与其他编程语言相比有很多优点,例如:
- 实现简单:GoLang中的接口是非常简单的,只需定义一组方法签名即可。
- 调用方便:因为接口是一种抽象类型,所以可以轻松实现多态调用。
- 扩展性强:接口可以被任何类型实现,因此具有良好的扩展性。
快速入门
package main
import "fmt"
// 声明一个接口
type Usb interface {
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
}
// 让Phone实现Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作")
}
func (p Phone) Stop() {
fmt.Println("手机结束工作")
}
type Computer struct {
}
func (c Computer) Working(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
computer := Computer{}
phone := Phone{}
computer.Working(phone)
}
注意事项
-
接口本身不能创建实例,但可以指向一个·实现了该接口的自定义类型的变量(实例)
package main import "fmt" type AInterface interface { Say() } type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("Stu Say()") } func main() { var stu Stu var a AInterface = stu a.Say() }
-
接口中所有的方法都没有方法体,即都是没有实现的方法
-
在GOlang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
-
一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
-
只要是自定义数据类型就可以实现接口,不仅是结构体类型
-
一个自定义类型可以实现多个接口
package main import "fmt" type AInterface interface { Say() } type BInterface interface { Hello() } type Monster struct { } func (m Monster) Hello() { fmt.Println("Monster Hello()") } func (m Monster) Say() { fmt.Println("Monster Say()") } func main() { var monster Monster var a AInterface = monster var b BInterface = monster a.Say() b.Hello() }
-
GOlang中接口不能有任何变量
-
一个接口可以继承多个别的接口。这时如果要实现A接口,也必须将B,C接口的方法也全部实现
package main import "fmt" type Binterface interface { test01() } type Cinterface interface { test02() } type Ainterface interface { Binterface Cinterface test03() } // 如果我们需要实现Ainterface,就需要将Binterface Cinterface的方法都实现 type Stu struct { } func (stu Stu) test01() { fmt.Println("test01") } func (stu Stu) test02() { fmt.Println("test02") } func (stu Stu) test03() { fmt.Println("test03") } func main() { var stu Stu var a Ainterface = stu a.test01() }
-
interface类型默认是一个指针(引用类型),如果没有对interface初始化就就使用,那么会输出nil
-
空接口interface没有任何方法,所有类型都实现了空接口 可以把任何变量赋给空接口
package main import "fmt" type Stu struct { } func (stu Stu) test01() { fmt.Println("test01") } func (stu Stu) test02() { fmt.Println("test02") } func (stu Stu) test03() { fmt.Println("test03") } type T interface { } func main() { var stu Stu var t T = stu fmt.Println(t) var t2 interface{} = stu num1 := 8.8 t2 = num1 fmt.Println(t2) }
接口运用实例
结构体切片按特定字段进行排序
package main
import (
"fmt"
"math/rand"
"sort"
)
type Hero struct {
Name string
Age int
}
type HeroSlice []Hero
func (hs HeroSlice) Len() int {
return len(hs)
}
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
}
func (hs HeroSlice) Swap(i, j int) {
temp := hs[i]
hs[i] = hs[j]
hs[j] = temp
}
func main() {
var heroes HeroSlice
for i := 0; i < 10; i++ {
hero := Hero{
Name: fmt.Sprintf("英雄~%d", rand.Intn(100)),
Age: rand.Intn(100),
}
heroes = append(heroes, hero)
}
for _, v := range heroes {
fmt.Println(v)
}
fmt.Println("==============")
sort.Sort(heroes)
for _, v := range heroes {
fmt.Println(v)
}
}
多态
接口来实现的
多态数组
package main
import (
"fmt"
)
// 声明一个接口
type Usb interface {
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
// 让Phone实现Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作")
}
func (p Phone) Stop() {
fmt.Println("手机结束工作")
}
type Camera struct {
name string
}
// 让Phone实现Usb接口的方法
func (p Camera) Start() {
fmt.Println("相机开始工作")
}
func (p Camera) Stop() {
fmt.Println("相机结束工作")
}
func main() {
//定义一个Usb接口数组,可以存放Phone和Camear的结构体变量
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"尼康"}
fmt.Println(usbArr)
}
类型断言
介绍
当我们在Go语言中使用接口类型时,有时候我们需要将接口类型转换为具体的类型。这就是类型断言(Type Assertion)。
快速入门
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
var a interface{}
var point Point = Point{1, 2}
a = point
var b Point
//b=a 不可以
b = a.(Point) //类型断言
fmt.Println(b)
}
//类型检测 检测是否可以强转 这样不会报错 需要自己判断
/*
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
var a interface{}
var point Point = Point{1, 2}
a = point
var b Point
var x bool
//b=a 不可以
b, x = a.(Point)
fmt.Println(b, x)
}
*/
实例
1
package main
import (
"fmt"
)
// 声明一个接口
type Usb interface {
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
// 让Phone实现Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作")
}
func (p Phone) Stop() {
fmt.Println("手机结束工作")
}
func (p Phone) Call() {
fmt.Println("手机 打电话ing")
}
type Camera struct {
name string
}
type Computer struct {
}
func (conputer Computer) Working(usb Usb) {
usb.Start()
if phone, ok := usb.(Phone); ok {
phone.Call()
}
usb.Stop()
}
// 让Phone实现Usb接口的方法
func (p Camera) Start() {
fmt.Println("相机开始工作")
}
func (p Camera) Stop() {
fmt.Println("相机结束工作")
}
func main() {
//定义一个Usb接口数组,可以存放Phone和Camear的结构体变量
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"尼康"}
var computer Computer
for _, v := range usbArr {
computer.Working(v)
fmt.Println()
}
}
2
package main
import "fmt"
func TypeJudge(items ...interface{}) {
for index, x := range items {
switch x.(type) {
case bool:
fmt.Printf("第%v个参数是 bool 类型,值是%v\n", index, x)
case string:
fmt.Printf("第%v个参数是 string 类型,值是%v\n", index, x)
case float64:
fmt.Printf("第%v个参数是 float64 类型,值是%v\n", index, x)
case float32:
fmt.Printf("第%v个参数是 float32 类型,值是%v\n", index, x)
case int, int32, int64:
fmt.Printf("第%v个参数是 整型 类型,值是%v\n", index, x)
case Student:
fmt.Printf("第%v个参数是 Student 类型,值是%v\n", index, x)
case *Student:
fmt.Printf("第%v个参数是 *Student 类型,值是%v\n", index, x)
default:
fmt.Printf("第%v个参数 类型不确定,值是%v\n", index, x)
}
}
}
type Student struct {
}
func main() {
var n1 = 3.2
var n2 float32 = 6.4
var n3 int32 = 30
var name string = "tom"
address := "背京"
n4 := 300
stu := Student{}
stu1 := &Student{}
TypeJudge(n1, n2, n3, name, address, n4, stu, stu1)
}
文件操作
基本介绍
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据源->程序
输出流:程序->数据源
常用的文件操作
打开文件 Open
func Open(name string)(file *File,err error)
关闭文件 Close
func (f *File) Close() error
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("./1.txt")
if err != nil {
fmt.Println("open file err=", err)
}
fmt.Printf("file=%v", file)
err = file.Close()
if err != nil {
fmt.Println("close file err=", err)
}
}
读取文件的内容并显示在终端(带缓冲区的方式)
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("./1.txt")
if err != nil {
fmt.Println("open file err=", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Printf("关闭文件失败")
}
}(file)
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
fmt.Printf(str)
if err == io.EOF {
break
}
}
}
一次性读取 返回切片
package main
import (
"fmt"
"os"
)
func main() {
file := "./1.txt"
countent, err := os.ReadFile(file)
if err != nil {
fmt.Printf("read file err=%v", err)
}
fmt.Printf("%v", string(countent))
}
文件权限
源码:src\os\file.go
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
文件模式·
src\io\fs\fs.go
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky // t: sticky
ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
ModePerm FileMode = 0777 // Unix permission bits
)
打开文件 OpenFile
func OpenFile(name string,flag int,perm FileMode)(file *File,err error)
第一个参数:文件名
第二个参数:文件权限 '|'用来连接
第三个参数:文件模式(linux)
写文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
filePath := "1.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err=%v", err)
return
}
str := "Hello,World\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
_, err := writer.WriteString(str)
if err != nil {
return
}
}
//writer是带缓存的
err = writer.Flush()
if err != nil {
fmt.Printf("Flush err=%v", err)
return
}
err = file.Close()
if err != nil {
fmt.Printf("close file err=%v", err)
return
}
}
func WriteFile(filename string,data []byte,prem os.FileMode) error
判断文件是否存在
package io
func Stat(name string)(file FileInfo,err error)
Stat返回一个描述name指定的文件对象的Fileinfo。如果指定的文件对象是个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,错误值为*PathError类型。
如果返回的错误类型用os.IsNotExist()判断为true,说明文件或文件夹不存在
如果是false则不确定是否存在
拷贝文件
package io
func Copy(dst Writer,src Reader)(written int64,err error)
返回拷贝的字节数和遇到的第一个error
func CopyFile(dstFilename string, srcFilename string) (written int64, err error) {
srcFile, err := os.Open(srcFilename)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer srcFile.Close()
reader := bufio.NewReader(srcFile)
dstFile, err := os.OpenFile(dstFilename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err=%v\n",err)
return
}
writer := bufio.NewWriter(dstFile)
defer dstFile.Close()
return io.Copy(writer, reader)
}
命令行参数
基本介绍
我们希望能够获得到命令行输入的各种参数
os.Args是一个string切片,可以用来存储所有的命令行参数
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("命令行的参数有", len(os.Args))
for i, v := range os.Args {
fmt.Printf("args[%v]=%v\n", i, v)
}
}
flag包
package main
import (
"flag"
"fmt"
)
func main() {
var user string
var pwd string
var host string
var port int
//&user 就是接收用户命令行中输入的-u 后面的参数值
//“u” 就是-u指定参数
//“” 默认值
// “用户名默认为空” 说明
flag.StringVar(&user, "u", "", "用户名默认为空")
flag.StringVar(&pwd, "pwd", "", "密码默认为空")
flag.StringVar(&host, "h", "localhost", "主机名默认为localhost")
flag.IntVar(&port, "port", 3306, "端口默认为3306")
//转换 必须调用该方法
flag.Parse()
fmt.Printf("user=%v pwd=%v host=%v port=%v\n", user, pwd, host, port)
}
json
序列化
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
func testStruct() {
monster := Monster{
Name: "牛魔王",
Age: 500,
Birthday: "2011-11-11",
Sal: 8000.0,
Skill: "牛魔拳",
}
data, err := json.Marshal(&monster)
if err != nil {
fmt.Println("序列化错误 err=", err)
}
fmt.Printf("monster序列化后=%v\n", string(data))
}
func testmap() {
var a map[string]interface{}
a = make(map[string]interface{})
a["name"] = "红孩儿"
a["age"] = 30
a["address"] = "火云洞"
data, err := json.Marshal(&a)
if err != nil {
fmt.Println("序列化错误 err=", err)
}
fmt.Printf("a序列化后=%v\n", string(data))
}
func testslice() {
var slice []map[string]interface{}
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "Jack"
m1["age"] = 18
m1["address"] = "北京"
slice = append(slice, m1)
var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "isn"
m2["age"] = 17
m2["address"] = "上海"
slice = append(slice, m2)
data, err := json.Marshal(&slice)
if err != nil {
fmt.Println("序列化错误 err=", err)
}
fmt.Printf("slice序列化后=%v\n", string(data))
}
func main() {
testStruct()
testmap()
testslice()
}
反序列化
json.Unmarshal([]byte(str),&obj)
反序列化map时不需要make
单元测试
快速入门
测试文件格式一定是???_test.go
package main
import "testing"
func TestAddUpper(t *testing.T) {
res := AddUpper(20)
if res != 55 {
t.Fatalf("期望值=%v 实际值=%v\n", 55, res)
}
t.Logf("执行正确")
}
go test -v
go test -v cal_test.go cal.go //测试单个文件 一定要带上被测试的原文件
go test -v test.run TestAddUpper //测试单个方法
go routine and channel
并发与并行
并发
多线程程序在单核上运行 就是并发
特点:
- 多个任务作用在一个Cpu
- 从微观的角度看,在一个时间点上,其实只有一个任务在执行
并行
多线程程序在多核上运行 就是并行
特点:
- 多个任务作用在多个Cpu
- 从微观角度看,在一个时间点,多个任务在同时执行
GO协程和主线程
-
Go主线程(有程序员直接称之为线程/可理解为进程); 一个Go线程上,可以起多个协程;协程是轻量级的线程(资源耗费相对较少)
-
特点:
有独立的栈空间
共享程序堆空间
调度由用户控制
协程是轻量级的线程
MPG模式
M:操作系统的主线程(是物理线程)
P:代表调度的上下文,可以把它看做一个局部的调度器,使 go 代码在一一个线程上跑,是实现从 N:1到 N:M 映射的关键。
G:协程
runtime包
NumCpu
func NumCpu()int
//返回本地计算器的逻辑cpu个数
GOMAXPROCS
func GOMAXPROCS(n int) int
//设置可同时执行最大CPU数
互斥锁
package main
import (
"fmt"
"sync"
)
var (
myMap = make(map[int]int64, 10)
lock sync.Mutex
)
func test(n int) {
var res int64 = 1
for i := 1; i <= n; i++ {
res *= int64(i)
}
lock.Lock()
myMap[n] = res
lock.Unlock()
}
func main() {
for i := 1; i <= 20; i++ {
go test(i)
}
for i, v := range myMap {
fmt.Println(i, v)
}
}
channel
基本介绍
- 本质就是数据结构的队列
- 先进先出 【FIFO: first in first out】
- 线程安全,多go routine访问时不需要加锁,就是channel本身时线程安全的
- channel由类型的 一个string的channel只能存放string类型数据
基本使用
var obj_name chan data_type
说明:
1. channel是引用类型
2. channel必须make后才能使用
3. 管道有类型 intChan只能写入整数int
写入&&读取数据
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
intChan <- 10
num := 211
intChan <- num
intChan <- 50
fmt.Printf("channel len=%v \ncap=%v\n", len(intChan), cap(intChan))
//3 3
var num2 int
num2 = <-intChan
fmt.Println(num2)
fmt.Printf("channel len=%v \ncap=%v\n", len(intChan), cap(intChan))
//2 3
}
注意事项
- channel只能存放指定数据类型
- channel的数据放满后,就不能再放入了
- 如果从channel中取出数据,则可以继续放入
- 在没有使用协程的情况下,如果channel数据取完了,再取,就会报deadlock
- go routine中使用recover,解决协程中出现panic,导致程序崩溃问题
关闭管道
一旦关闭 只能读不能写
close(objChan)
遍历管道
for-range遍历
- 在遍历时 如果channel没有关闭,则会出现deadlock错误
- 在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
只读只写
var chan2 chan<- int //只写
var chan3 <-chan int //只读
select
select可以解决从管道取数据堵塞的问题
package main
import "fmt"
func main() {
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
intChan2 := make(chan int, 10)
for i := 10; i < 20; i++ {
intChan2 <- i
}
label:
for {
select {
case v := <-intChan:
fmt.Println("intChan读取数据为", v)
case v := <-intChan2:
fmt.Println("intChan2读取数据为", v)
default:
fmt.Println("都取不到")
break label
}
}
}
routine&&channel
package main
import (
"fmt"
)
func writeData(intChan chan int) {
for i := 0; i < 50; i++ {
intChan <- i
fmt.Printf("readData 写入数据=%v\n", i)
}
close(intChan)
}
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan
if !ok {
break
}
fmt.Printf("readData 读到数据=%v\n", v)
}
exitChan <- true
close(exitChan)
}
func main() {
intChan := make(chan int, 50)
boolChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, boolChan)
for range boolChan {
}
}
多线程求素数
package main
import (
"fmt"
"math"
"time"
)
func putNum(intChan chan int) {
for i := 1; i <= 8000; i++ {
intChan <- i
}
close(intChan)
}
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
flag := true
for {
time.Sleep(time.Millisecond * 10)
num, ok := <-intChan
if !ok {
break
}
flag = true
for i := 2; i < int(math.Sqrt(float64(num)))+1; i++ {
if num%i == 0 {
flag = false
break
}
}
if flag {
primeChan <- num
}
}
fmt.Println("有一个协程结束")
exitChan <- true
}
func main() {
intChan := make(chan int, 1000)
primeChan := make(chan int, 2000)
exitChan := make(chan bool, 4)
go putNum(intChan)
for i := 0; i < 4; i++ {
go primeNum(intChan, primeChan, exitChan)
}
go func() {
for i := 0; i < 4; i++ {
<-exitChan
}
close(primeChan)
}()
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Printf("素数=%d\n", res)
}
}
反射
基本介绍
- 反射可以在运动时动态获取变量的各种信息,比如变量的类型(type)
- 如果时结构体变量,还可以获取到结构体本身的信息(包括结构体的字段 方法)
- 通过反射,可以修改变量的值,可以调用关联的方式。
- 使用反射,需要import "reflect"
TypeOf
reflect.TypeOf(obj) //获取obj类型 返回reflect.Type类型
ValueOf
reflect.ValueOf(obj) //获取变量的值,返回reflect.Value->结构体类型
interface{}和reflect.Value相互转换
rVal := reflect.Value(b) //interface{} -> reflect.Value
iVal :=rVal.Interface() //reflect.Value -> interface{}
type接口
/src/reflect/type.go
type Type interface {
// Methods applicable to all types.
// Align returns the alignment in bytes of a value of
// this type when allocated in memory.
Align() int
// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign() int
// Method returns the i'th method in the type's method set.
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver,
// and only exported methods are accessible.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
//
// Methods are sorted in lexicographic order.
Method(int) Method
// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)
// NumMethod returns the number of methods accessible using Method.
//
// For a non-interface type, it returns the number of exported methods.
//
// For an interface type, it returns the number of exported and unexported methods.
NumMethod() int
// Name returns the type's name within its package for a defined type.
// For other (non-defined) types it returns the empty string.
Name() string
// PkgPath returns a defined type's package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
// If the type was predeclared (string, error) or not defined (*T, struct{},
// []int, or A where A is an alias for a non-defined type), the package path
// will be the empty string.
PkgPath() string
// Size returns the number of bytes needed to store
// a value of the given type; it is analogous to unsafe.Sizeof.
Size() uintptr
// String returns a string representation of the type.
// The string representation may use shortened package names
// (e.g., base64 instead of "encoding/base64") and is not
// guaranteed to be unique among types. To test for type identity,
// compare the Types directly.
String() string
// Kind returns the specific kind of this type.
Kind() Kind
// Implements reports whether the type implements the interface type u.
Implements(u Type) bool
// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
// Even if ConvertibleTo returns true, the conversion may still panic.
// For example, a slice of type []T is convertible to *[N]T,
// but the conversion will panic if its length is less than N.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
// Even if Comparable returns true, the comparison may still panic.
// For example, values of interface type are comparable,
// but the comparison will panic if their dynamic type is not comparable.
Comparable() bool
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Pointer: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// ChanDir returns a channel type's direction.
// It panics if the type's Kind is not Chan.
ChanDir() ChanDir
// IsVariadic reports whether a function type's final input parameter
// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
// implicit actual type []T.
//
// For concreteness, if t represents func(x int, y ... float64), then
//
// t.NumIn() == 2
// t.In(0) is the reflect.Type for "int"
// t.In(1) is the reflect.Type for "[]float64"
// t.IsVariadic() == true
//
// IsVariadic panics if the type's Kind is not Func.
IsVariadic() bool
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
Elem() Type
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
FieldByIndex(index []int) StructField
// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
FieldByName(name string) (StructField, bool)
// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
// and then the fields in any embedded structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go's handling of name lookup in
// structs containing embedded fields.
FieldByNameFunc(match func(string) bool) (StructField, bool)
// In returns the type of a function type's i'th input parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumIn()).
In(i int) Type
// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
// Len returns an array type's length.
// It panics if the type's Kind is not Array.
Len() int
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int
// NumIn returns a function type's input parameter count.
// It panics if the type's Kind is not Func.
NumIn() int
// NumOut returns a function type's output parameter count.
// It panics if the type's Kind is not Func.
NumOut() int
// Out returns the type of a function type's i'th output parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumOut()).
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
快速入门
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}) {
//通过反射获取传入变量的type kind
rType := reflect.TypeOf(b)
fmt.Println("rType=", rType)
rValue := reflect.ValueOf(b)
fmt.Println("rValue=", rValue)
fmt.Println("rValue+2=", rValue.Int()+2)
iv := rValue.Interface()
num2 := iv.(int)
fmt.Println("num2=", num2)
}
func main() {
var num = 100
reflectTest01(num)
}
package main
import (
"fmt"
"reflect"
)
type Student struct {
name string
Age int
}
func reflectTest01(b interface{}) {
//通过反射获取传入变量的type kind
rType := reflect.TypeOf(b)
fmt.Println("rType=", rType)
rValue := reflect.ValueOf(b)
fmt.Println("rValue=", rValue)
iv := rValue.Interface()
fmt.Printf("iv=%v T=%T\n", iv, iv)
fmt.Println("rType.Kind=", rType.Kind())
fmt.Println("rValue.Kind=", rValue.Kind()) //获得变量的类别 返回的是常量
}
func main() {
stu := Student{"li", 18}
reflectTest01(stu)
}
//rType= main.Student
//rValue= {li 18}
//iv={li 18} T=main.Student
//rType.Kind= struct
//rValue.Kind= struct
注意事项
-
reflect.Value.Kind()获得变量的类别 返回一个常量
-
Type是类型 Kind是类别 可能相同也可能不同 例如结构体就不同
-
使用反射获取变量值 要求类型匹配 x是int 你就用reflect.Value.Int()
-
通过反射来修改变量 当使用SetXxx方法来设置需要通过对应指针类型来完成,这样才能改变传入变量的值,同时需要使用reflect.Value.Elem()
val.Elem().SetInt(10)
-
//fn.Elem() var num=20 var b *int =&num *b=10