1 文件操作
1.1 简介
import "os"
1.2 常用的操作API
// File代表一个打开的文件对象
type File struct {
// 内含隐藏或非导出字段
}
// 打开文件
func Open(name string) (file *File, err error)
// 关闭文件
func (f *File) Close() error
1.3 读写操作
带缓冲的读取
func readFile() {
// [1] 打开
file, err := os.Open("E:\\ShareDir\\Code\\GoCode\\src\\go_code\\proj07_file\\test.txt")
if err != nil{
fmt.Printf("err:%s\n", err)
}
// [end] 延时关闭
defer file.Close()
// 创建一个带缓冲的*Read 默认大小:4096
reader := bufio.NewReader(file)
// 循环读取
for{
readString, err := reader.ReadString('\n') // 按换行符读取
if err == io.EOF{
break // 读取完毕
}
// 输出内容
fmt.Printf("%s", readString)
}
}
一次性读取
一次将文件内容全部读取到内存中
func readFileInOnece() {
filePath := "E:\\ShareDir\\Code\\GoCode\\src\\go_code\\proj07_file\\test.txt"
// func ReadFile(filename string) ([]byte, error)
file, _ := ioutil.ReadFile(filePath)
str := string(file)
fmt.Println(str)
}
创建文件并写内容
const (
O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
O_RDWR int = syscall.O_RDWR // 读写模式打开文件
O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
)
func writeToFile() {
filePath := "E:\\ShareDir\\Code\\GoCode\\src\\go_code\\proj07_file\\newtest.txt"
// 打开创建新文件
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil{
fmt.Printf("err:%s\n", err)
}
// 延时关闭
defer file.Close()
// 循环写入 使用带缓冲的写
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++{
writer.WriteString(fmt.Sprintf("xhh[%d]\r\n", i))
fmt.Println("ok---")
}
// 需要将缓存刷入文件
writer.Flush()
}
1.4 判断文件(夹)是否存在
// Stat返回一个描述name指定的文件对象的FileInfo。
// 如果出错,返回的错误值为*PathError类型。
func Stat(name string) (fi FileInfo, err error)
type FileInfo interface {
Name() string // 文件的名字(不含扩展名)
Size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
Mode() FileMode // 文件的模式位
ModTime() time.Time // 文件的修改时间
IsDir() bool // 等价于Mode().IsDir()
Sys() interface{} // 底层数据来源(可以返回nil)
}
1.5 拷贝文件
- 文本
func copyFile() {
var srcPath = "E:\\ShareDir\\Code\\GoCode\\src\\go_code\\proj07_file\\test.txt"
// find 后缀位置
var pointIndex = strings.LastIndex(srcPath, ".")
var newPath = srcPath[:pointIndex] + "backup" + srcPath[pointIndex:]
fmt.Println(srcPath)
fmt.Println(newPath)
// [1] openSrc
openFile, err := os.OpenFile(srcPath, os.O_RDONLY, 0666)
if err != nil{
fmt.Println("open err : ", err)
return
}
// 延时关闭
defer openFile.Close()
reader := bufio.NewReader(openFile)
// [2] openDst
writeFile, err := os.OpenFile(newPath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil{
fmt.Println("open err : ", err)
return
}
defer writeFile.Close()
writer := bufio.NewWriter(writeFile)
// [3] 循环读写
for{
// 读取
readString, err := reader.ReadString('\n')
if err == io.EOF{
// 读取完毕
break
}
// 写入和刷盘
_, err = writer.WriteString(readString)
if err != nil{
fmt.Println("writer err : ", err)
return
}
err = writer.Flush()
if err != nil{
fmt.Println("writer.Flush() err : ", err)
return
}
}
fmt.Printf("copy success!\n")
}
- 图片或者视频
// 从src拷贝n个字节数据到dst,直到在src上到达EOF或发生错误。
// 返回复制的字节数和遇到的第一个错误。
// 对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。
// 如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
func Copy(dst Writer, src Reader) (written int64, err error)
func copyImg() {
var srcPath = "E:\\ShareDir\\Code\\GoCode\\src\\go_code\\proj07_file\\src.jpg"
// find 后缀位置
var pointIndex = strings.LastIndex(srcPath, ".")
var dstPath = srcPath[:pointIndex] + "_bak" + srcPath[pointIndex:]
// func Copy(dst Writer, src Reader) (written int64, err error)
// [1] Reader
readFile, err := os.Open(srcPath)
if err != nil{
fmt.Println("Open err : ", err)
return
}
defer readFile.Close()
reader := bufio.NewReader(readFile)
// [2] Writer
writeFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil{
fmt.Println("OpenFile err : ", err)
return
}
defer writeFile.Close()
writer := bufio.NewWriter(writeFile)
// [3] copy
_, err = io.Copy(writer, reader)
if err != nil{
fmt.Println("io.Copy err : ", err)
return
}
fmt.Printf("Copy img success.\n")
}
2 json
2.1 简介
2.2 json的序列化(serialize)
结构体、map、切片
使用tag
// import "encoding/json"
// Marshal函数返回v的json编码。
func Marshal(v interface{}) ([]byte, error)
package main
import (
"encoding/json"
"fmt"
)
// 定义结构体
type Person struct {
Name string
Age int
}
func structSerailze() {
// struce -> seralize
p1 := Person{"xhh", 18}
// 序列化
data, err := json.Marshal(&p1)
if err != nil{
fmt.Printf("序列化 err %v\n", err)
return
}
fmt.Printf("序列化效果:%v\n", string(data))
}
func mapSerailze() {
// map -> seralize
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "xhh"
m1["age"] = 18
m1["info"] = "abcdef"
// 序列化 map 引用类型 本身就是地址 不需要&m1
data, err := json.Marshal(m1)
if err != nil{
fmt.Printf("序列化 err %v\n", err)
return
}
fmt.Printf("序列化效果:%v\n", string(data))
}
func silceSerailze() {
// slice -> seralize
var s1 []map[string]interface{}
// values
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "xhh"
m1["age"] = 18
var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "mcy"
m2["age"] = 4
// append values
s1 = append(s1, m1, m2)
// 序列化 map 引用类型 本身就是地址 不需要&m1
data, err := json.Marshal(s1)
if err != nil{
fmt.Printf("序列化 err %v\n", err)
return
}
fmt.Printf("序列化效果:%v\n", string(data))
}
func main() {
// struct map slice -> serailze
structSerailze()
mapSerailze()
silceSerailze()
// 序列化效果:{"Name":"xhh","Age":18}
// 序列化效果:{"age":18,"info":"abcdef","name":"xhh"}
// 序列化效果:[{"age":18,"name":"xhh"},{"age":4,"name":"mcy"}]
}
- tag
使用反射机制,实现序列化时的key替换
// 定义结构体
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
2.3 json的反序列化
// 反序列化
func unmarshalStruct() {
// 网络得到的序列化str
str := "{\"Name\":\"xhh\",\"Age\":18}"
// 定义一个结构体变量
var p1 Person
// 反序列化为 赋值到p1
// func Unmarshal(data []byte, v interface{}) error
err := json.Unmarshal([]byte(str), &p1)
if err != nil{
fmt.Printf("反序列化 err %v\n", err)
return
}
fmt.Printf("unmarshalStruct name[%s] age[%d]\n", p1.Name, p1.Age)
}
3 单元测试、goroutine、channel
单元测试:测试一个函数或者模块
3.1 testing测试框架
import "testing"
3.2 单元测试细节
3.3 goroutine(协程)简介
传统方法:单核进行,很慢
单CPU运行(并发),多CPU(并行)
3.4 goroutine 快速入门、MPG模型
以主线程结束为主
package main
import (
"fmt"
"time"
)
func routFunc() {
for i := 0; i < 10; i++{
time.Sleep(time.Millisecond * 500)
fmt.Printf("Hello go_routione %d\n", i)
}
}
func main() {
// 10次 每个 1s
// 主线程 打印 go_land
// 协程 打印 go_routione
// 开启一个协程
// 交替执行 主线程执行完毕结束
go routFunc()
for i := 0; i < 5; i++{
time.Sleep(time.Millisecond * 500)
fmt.Printf("Hello go_land %d\n", i)
}
}
- M:主线程(物理线程)
- P:线程执行上下文
- G:协程
设置运行CPU个数
3.5 goroutine 通信、并发
// 多协程并发计算 1-200的阶乘 放入map
问题一:主线程等待协程多长时间?
问题二:同时写入,存在并发竞争关系?(采用全局锁或者管道)
// 查看竞争
go build -race m2.go
3.6 【新手】sync
- sync
// sync包提供了基本的同步基元,如互斥锁。
// 除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些。
import "sync"
package main
import (
"fmt"
"sync"
"time"
)
// 全局map
var(
mapii = make(map[int]int)
// 全局互斥锁
gLock sync.Mutex
)
// 计算 n! 放入map
func calcJC(n int) {
res := 1
for i := 1; i <= n; i++{
res *= i
}
// 放入map [1] 加锁和解锁
gLock.Lock()
mapii[n] = res
gLock.Unlock()
}
func main() {
// 多协程并发计算 1-200的阶乘 放入map
for i := 1; i <= 50; i++{
go calcJC(i)
}
time.Sleep(time.Millisecond * 3000)
// 遍历 读取数据时 加锁
gLock.Lock()
for idx, val := range mapii{
fmt.Printf("mapii[%d]=%d\n", idx, val)
}
gLock.Unlock()
}
3.7【荐】channel
channel(管道:数据结构中的队列)
不需要加锁,channel是线程安全的
channel是有类型的,只能放置声明的类型
基本使用
chan 需要make初始化,才能使用
chan 有类型
// 存放 int
var intChan chan int
// 存放 map
var mapChan chan map[int]string
创建管道,取数据,存数据
package main
import "fmt"
func main() {
// [1] make存放容量cap 3 的chan
var intChan chan int = make(chan int, 3)
// [2] 查看类型 intChan type : chan int v: 0xc04206a080 -> (是个地址,说明是个引用类型)
fmt.Printf("intChan type : %T v: %v\n", intChan, intChan)
// [3] 向管道写入 不允许超过容量
intChan <- 11
intChan <- 12
// [4] 查看大小和容量
fmt.Printf("intChan size:%v cap:%v\n", len(intChan), cap(intChan))
// [5] 取出数据 不使用协程的时候 从空chan取数据 则 deadlock
num := <- intChan
fmt.Printf("num : %v\n", num)
}
3.8 通道的打开和关闭
关闭:只能读,不能写
// func close(c chan<- Type)
close(intChan) // close 通道
package main
import "fmt"
func main() {
intChan := make(chan int, 5)
intChan <- 11
intChan <- 44
// [1] close
close(intChan)
// [2] 遍历
for v := range intChan{
fmt.Printf("value : %v\n", v)
}
}
读和写协程
package main
import (
"fmt"
"time"
)
// 写入
func writeData(intChan chan int) {
for i := 0; i < 20; i++{
intChan<- i
fmt.Printf("writeData 写入 [%d]\n", i)
time.Sleep(time.Millisecond * 100)
}
// 写入30个数据,关闭通道,这样读取到末尾会退出
close(intChan)
}
// 读取
func readData(intChan chan int, exitChan chan bool) {
for{
// 若没有数据会进行阻塞
val, ok := <-intChan
if !ok{
// 读到了 close 关闭的末尾
break
}
fmt.Printf("readData 读取 [%d]\n", val)
time.Sleep(time.Millisecond * 200)
}
// 退出通道标志 发送为主线程用
exitChan<- true
close(exitChan)
}
func main() {
// [1] 数据通道 退出通道
intChan := make(chan int, 50)
exitChan := make(chan bool, 1)
// [2] 读 和 写 的协程
go writeData(intChan)
go readData(intChan, exitChan)
// [3] exitChan 阻塞在此处,等待读取数据
// 直到 exitChan 中有数据,就进行读取
// 当exitChan 关闭时 此处的循环退出,主线程结束
for v := range exitChan{
fmt.Printf("读取到了 %v\n", v)
}
fmt.Printf("Main thread exit\n")
}
3.9 案例:多协程同步
整空间为4的通道来控制4个协程的退出
package main
import "fmt"
func putNum(intChan chan int) {
for i := 1; i <= 8000; i++ {
intChan<- i
}
// 关闭intChan
close(intChan)
}
func getPrime(intChan chan int, primeChan chan int, eixtChan chan bool) {
for{
// 没有关闭管道,并且管道为空,这里会阻塞
num, ok := <-intChan
if !ok{
// intChan管道【关闭】 并且读取到【末尾】
// 退出处理
break
}
isPrime := true
//判断是否为prime
for i := 2; i < num; i++{
if num % i == 0{
isPrime = false
break
}
}
if isPrime{
// 加入集合
primeChan<- num
}
}
// 当前协程退出(放入数组 说明自己退出)
// 不能关闭 primeChan 因为不知道其他协程是否完成
eixtChan<- true
}
func main() {
intChan := make(chan int, 1000) // 放数据
primeChan := make(chan int, 2000) // 放结果
exitChan := make(chan bool, 4) // 标识4个线程
// 开启协程 放1-8000的数 容量是1000 所以会阻塞进行
go putNum(intChan)
// 开启4个协程 进行同时处理
for i := 0; i < 4; i++ {
go getPrime(intChan, primeChan, exitChan)
}
// 主线程在此处进行阻塞 等待协程完成
// 读取四次exitChan 表示4个协程全部退出了
// 如果没读取那么多 说明协程还在工作 此处阻塞
for i := 0; i < 4; i++ {
_ = <-exitChan
}
// 4个协程工作完毕后 关闭素数primeChan通道
close(primeChan)
// 读取计算后的数据
for val := range primeChan{
fmt.Printf("prime val : %d\n", val)
}
}
3.10 管道阻塞机制
不关闭通道,或者不读会产生 deadlock
读空了,或者加入满了,会进行阻塞
func calcNum(numChan chan int, fnChan chan Fn, exitChan chan bool) {
for{
//time.Sleep(time.Millisecond * 500)
// 取得 num
// [1] 通道空 阻塞此处
// [2] 通道关闭 -> false 退出
num, ok := <-numChan
if !ok{
exitChan<- true
fmt.Printf("Exit calcNum(?) add bool to exitChan\n")
break
}
// 计算 resNum
// Fn 创建并放入FnChan
fn := Fn{num, calcN(num)}
fnChan<- fn
fmt.Printf("calcN(%d)\n", num)
}
}
3.11 管道的注意细节
读写说明
// [1] 读写
chan1 := make(chan int, 3)
// [2] 只读
chan2 := make(<-chan int, 3)
// [3] 只写
chan1 := make(chan<- int, 3)
select可以解决管道取数据阻塞的问题
注意:select不阻塞,使用for-select进行包裹
使用xhhlabel和bread xhhlable进行跳出循环
package main
import (
"fmt"
"time"
)
func main() {
// select解决管道取数据阻塞的问题
// [1] intChan
intChan := make(chan int, 2)
intChan<- 11
intChan<- 33
// [2] stringChan
stringChan := make(chan string, 2)
stringChan<- "xhh123"
stringChan<- "mcy456"
// [-- old]传统方法读取不关闭的通道,会产生 deadlock
// 并且不能使用
//for val := range intChan{
// fmt.Printf("intChan : %v\n", val)
//}
//for val := range stringChan{
// fmt.Printf("intChan : %v\n", val)
//}
// [-- new]使用select方法解决阻塞
// 实际开发可能不知道什么时候阻塞
xhhlabel:
for{
select {
case v := <-intChan:
fmt.Printf("[select] intChan : %v\n", v)
time.Sleep(time.Millisecond * 500)
case v := <-stringChan:
fmt.Printf("[select] stringChan : %v\n", v)
time.Sleep(time.Millisecond * 500)
default:
fmt.Printf("[select] default : no val\n")
time.Sleep(time.Millisecond * 500)
break xhhlabel // 退出for循环
// return // 退出函数
}
}
fmt.Printf("main exit sys.\n")
}
/*
[select] stringChan : xhh123
[select] intChan : 11
[select] intChan : 33
[select] stringChan : mcy456
[select] default : no val
main exit sys.
*/
解决协程中的panic
panic导致程序奔溃的问题
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 10; i++ {
fmt.Printf("say Hello\n")
time.Sleep(time.Millisecond * 500)
}
}
func errFunc() {
// ------------------------------------------------------------
// 使用 defer+recover 解决 panic: assignment to entry in nil map
defer func() {
// 捕获异常
if err := recover(); err != nil{
fmt.Printf("errFunc 产生错误 err[%s]\n", err)
}
}()
// ------------------------------------------------------------
// 定义一个错误
var m1 map[int]string
// m1 = make(map[int]string)
// 没有make进行使用 会产生错误err
m1[1] = "xhh"
}
func main() {
go sayHello() // panic: assignment to entry in nil map
go errFunc() // errFunc协程出现错误 导致 sayHello协程 和 整个程序结束
time.Sleep(time.Millisecond * 10000) // 主线程休眠10s防止线程退出
}
/*
say Hello
errFunc 产生错误 err[assignment to entry in nil map]
say Hello
say Hello
*/
4 反射
reflection
4.1 简介
// reflect.Func(...)
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
变量、interface{}、reflect.Value相互转换
4.2 快速入门案例(基本类型、结构体)
对基本类型操作
package main
import (
"fmt"
"reflect"
)
func changeType(i interface{}) int {
// [1] 函数传入 类型 int -> interface{}
// [2] interface{} -> reflect.Value
rType := reflect.TypeOf(i)
// rValue val : int
fmt.Printf("rValue val : %v\n", rType)
rValue := reflect.ValueOf(i)
// rValue val : 99 type : reflect.Value 这里的rValue的类型还是 reflect.Value
fmt.Printf("rValue val : %v type : %T\n", rValue, rValue)
// [3] reflect.Value -> int 获取值
numSrc := rValue.Int()
fmt.Printf("numSrc type : %T\n", numSrc)
// [4] reflect.Value -> interface{}
rValueInter := rValue.Interface()
// [5] interface{} -> 断言成int
numEnd := rValueInter.(int)
return numEnd
}
func main() {
num := 99
fmt.Printf("num type : %T\n", num)
_ = changeType(num)
}
对结构体的操作
package main
import (
"fmt"
"reflect"
)
type Cat struct {
Name string
Age int
}
func changeCat(i interface{}) {
// 传入 Cat -> interface{}
// [1] 获取 reflect.Type rType: main.Cat
rType := reflect.TypeOf(i)
fmt.Println("rType: ", rType)
// [2] 获取 reflect.Value rValue val : {xhh 18} type : reflect.Value
rValue := reflect.ValueOf(i)
fmt.Printf("rValue val : %v type : %T\n", rValue, rValue)
// rValue type : main.Cat kind : struct
// type(包名) kind(结构体)
fmt.Printf("rValue type : %v kind : %v\n", rValue.Type(), rValue.Kind())
// [3] reflect.Value -> interface{}
rICat := rValue.Interface()
// [4] 断言获取原类型变量 执行
if c2, ok := rICat.(Cat); ok{
// 断言成功 显示信息
fmt.Printf("Cat name:%s age:%d\n", c2.Name, c2.Age)
}
}
func main() {
c1 := Cat{"xhh", 18}
changeCat(c1)
}
断言的最佳实践
常量
常量的访问范围还是有首字符大小的要求
没有全部大写的要求
4.3 反射的细节
- Kind(类别):都是struct
- Type(类型):pkg.Cat
- 类别和类型可能相同,可能不同
4.4 设置反射字段的值
// func ValueOf(i interface{}) Value
// Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。
// 如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
func (v Value) Elem() Value
- 利用地址和Elem修改
package main
import (
"fmt"
"reflect"
)
type Cat struct {
Name string
Age int
}
func setIntVal(i interface{}) {
rVal := reflect.ValueOf(i)
// [Int-2] .Elem() 得到指针 然后去修改值
rVal.Elem().SetInt(99)
}
func setCat(i interface{}) {
// 传入 Cat -> interface{}
// 获取 reflect.Value rValue val : {xhh 18} type : reflect.Value
rValue := reflect.ValueOf(i)
rValue.Elem().FieldByName("Name").SetString("abcd")
// 这样也可以修改
//cat := i.(*Cat)
//cat.Name = "abc"
}
func main() {
// - 整形的修改
num := 1
fmt.Printf("src num : %d\n", num)
setIntVal(&num) // [Int-1] 注意这里传入的是地址
fmt.Printf("dst num : %d\n", num)
// - 结构体的修改
c1 := Cat{"xhh", 18}
fmt.Println("src Cat : ", c1)
setCat(&c1)
fmt.Println("dst Cat : ", c1)
}
/*
src num : 1
dst num : 99
src Cat : {xhh 18}
dst Cat : {abcd 18}
*/
4.5 反射的最佳实践(使用反射获取结构体的方法和标签tag值)
// 字段个数
func (v Value) NumField() int
// 第i个字段的值
func (v Value) Field(i int) Value
// 获得第i个方法 从0开始
func (v Value) Method(i int) Value
// 调用函数
func (v Value) Call(in []Value) []Value
- Code
package main
import (
"fmt"
"reflect"
)
//定义了一个Monster结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32 `json:"成绩"`
Sex string
}
//方法,返回两个数的和
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
//方法, 接收四个值,给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
//方法,显示s的值
func (s Monster) Print() {
fmt.Println("---start~----")
fmt.Println(s)
fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
//获取reflect.Type 类型
typ := reflect.TypeOf(a)
//获取reflect.Value 类型
val := reflect.ValueOf(a)
//获取到a对应的类别
kd := val.Kind()
//如果传入的不是struct,就退出
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
//获取到该结构体有几个字段
num := val.NumField()
fmt.Printf("struct has %d fields\n", num) // 4
//变量结构体的所有字段
for i := 0; i < num; i++ {
fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i))
//获取到struct标签, 注意需要通过reflect.Type来获取tag标签的值
tagVal := typ.Field(i).Tag.Get("json")
//如果该字段于tag标签就显示,否则就不显示
if tagVal != "" {
fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
}
}
//获取到该结构体有多少个方法
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods\n", numOfMethod)
//var params []reflect.Value
//方法的排序默认是按照 函数名的排序(ASCII码)
val.Method(1).Call(nil) //获取到第二个方法。调用它
//调用结构体的第1个方法Method(0)
var params []reflect.Value //声明了 []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Value
fmt.Println("res=", res[0].Int()) //返回结果, 返回的结果是 []reflect.Value*/
}
func main() {
//创建了一个Monster实例
var a Monster = Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
}
//将Monster实例传递给TestStruct函数
TestStruct(a)
}
/*
struct has 4 fields
Field 0: 值为=黄鼠狼精
Field 0: tag为=name
Field 1: 值为=400
Field 1: tag为=monster_age
Field 2: 值为=30.8
Field 2: tag为=成绩
Field 3: 值为=
struct has 3 methods
---start~----
{黄鼠狼精 400 30.8 }
---end~----
res= 50
进程完成,并显示退出代码 0
*/
4.6 修改、打印字段,调用方法
修改、打印字段,调用方法
package main
import (
"fmt"
"reflect"
)
type Nums struct {
Num1 int `json:"xhh_num1"`
Num2 int `json:"xhh_num2"`
}
func (nums Nums) GetAdd(user string) int {
fmt.Printf("[%v] is coming func (nums Nums) GetAdd(info string) int\n", user)
return nums.Num1 + nums.Num2
}
func editNums(i interface{}) {
// [0] 获得 reflect.Value 类型的 .Elem 指针类型
iElem := reflect.ValueOf(i).Elem()
// [2] 设置字段
iElem.FieldByName("Num1").SetInt(88)
iElem.FieldByName("Num2").SetInt(99)
// [3] 打印字段 打印 Tag
for i := 0; i < iElem.NumField(); i++ {
fmt.Printf("iElem.Field(i) : %v\n", iElem.Field(i))
/*
iElem.Field(i) : 88
iElem.Field(i) : 99
*/
}
iTypeElem := reflect.TypeOf(i).Elem()
for i := 0; i < iTypeElem.NumField(); i++ {
fmt.Printf("iTypeElem.Field(i).Tag.Get(\"json\") : %v\n", iTypeElem.Field(i).Tag.Get("json"))
/*
iTypeElem.Field(i).Tag.Get("json") : xhh_num1
iTypeElem.Field(i).Tag.Get("json") : xhh_num2
*/
}
// [4] 调用方法
fmt.Printf("iElem.NumMethod() = %d\n", iElem.NumMethod())
rGetAdd := iElem.MethodByName("GetAdd")
var params []reflect.Value
params = append(params, reflect.ValueOf("xhh"))
rets := rGetAdd.Call(params)
// rets 88 + 99 = 187
fmt.Printf("rets %d + %d = %d\n",
iElem.FieldByName("Num1").Int(),
iElem.FieldByName("Num2").Int(),
rets[0].Int())
}
func main() {
nums := Nums{
Num1: 11,
Num2: 22,
}
fmt.Printf("not edit.Num1:%d .Num2:%d\n", nums.Num1, nums.Num2)
editNums(&nums)
fmt.Printf("after edit.Num1:%d .Num2:%d\n", nums.Num1, nums.Num2)
}
/*
not edit.Num1:11 .Num2:22
iElem.Field(i) : 88
iElem.Field(i) : 99
iTypeElem.Field(i).Tag.Get("json") : xhh_num1
iTypeElem.Field(i).Tag.Get("json") : xhh_num2
iElem.NumMethod() = 1
[xhh] is coming func (nums Nums) GetAdd(info string) int
rets 88 + 99 = 187
after edit.Num1:88 .Num2:99
*/
new 新的变量
package main
import (
"fmt"
"reflect"
)
type Dog struct {
Name string
Age int
}
// func (v Value) Elem() Value
/*
Elem返回v持有的接口保管的值的 Value封装,或者v持有的指针指向的值的 Value封装。
如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
*/
func TestCreateDog() {
var (
dogPtr *Dog
dogT reflect.Type
dogV reflect.Value
)
// [1] 获取类型 *Dog
dogT = reflect.TypeOf(dogPtr)
fmt.Printf(".Kind() : %v\n", dogT.Kind().String()) // ptr 类型
// [2] 指向 持有的类型 struct
dogT = dogT.Elem()
fmt.Printf(".Kind() : %v\n", dogT.Kind().String()) // struct 类型
// [3] 返回Value类型 持有Type类型 新申请的0指针
// 这里已经得到了反射的 ptr 但是未得到对象 *Dog 去指向他
dogV = reflect.New(dogT)
fmt.Printf("dogV.Kind() : %v\n", dogV.Kind().String()) // ptr 种类 Value类型
// 类型 均为 reflect.Value 种类Kind 有ptr、Struct
// [使用的是 ptr种类 需要 .Elem()拿到值]---------------------------------
dogV.Elem().FieldByName("Name").SetString("abc")
dogV.Elem().FieldByName("Age").SetInt(18)
fmt.Printf("dog.Name = %v dog.Age = %v\n",
dogV.Elem().FieldByName("Name"), // ptr 种类通过 .Elem()拿到值
dogV.Elem().FieldByName("Age"))
// [使用的是 ptr种类 需要 .Elem()拿到值]---------------------------------
// [4] 得到对象 指针类型 ptr
dogPtr = dogV.Interface().(*Dog) // *Dog
fmt.Printf("dog.Name = %v dog.Age = %v\n", dogPtr.Name, dogPtr.Age)
// [ptr种类通过 .Elem()转为 struct种类 直接访问或者修改]--------------------
dogV = dogV.Elem() // struct 种类 把dogV从ptr种类转为 struct种类
fmt.Printf("dogV.Kind() : %v\n", dogV.Kind().String()) // struct 种类
fmt.Printf("dog.Name = %v dog.Age = %v\n",
dogV.FieldByName("Name"),
dogV.FieldByName("Age"))
// [ptr类型通过 .Elem()转为 Value类型 直接访问或者修改]--------------------
}
func main() {
TestCreateDog()
}
/*
.Kind() : ptr
.Kind() : struct
dogV.Kind() : ptr
dog.Name = abc dog.Age = 18
dog.Name = abc dog.Age = 18
dogV.Kind() : struct
dog.Name = abc dog.Age = 18
*/