go 基础学习总结

< 个人复习使用 >
go语言基础

1.go语言的注意点

  • 变量名大写,意味着变量是包外可见的,可以跨包使用。
  • go语言的四个主要声明:变量var、常量const、类型type、函数func。

2.变量

  • 声明
    var name type = expression 类型和表达式(初始化)可以省略,但不能全部都省略,如果变量未初始化,默认以零值初始化。
    name := expreesion 短变量声明,可以用来声明和初始化局部变量。

  • 基本变量:uintxx(xx表示数据的大小16、32…)、浮点数floatxx、复数、布尔值、字符串(字符串是不可变序列,对字符串操作重要的包bytes、strings、strconv、unicode)、常量(go特有无类型常量)

  • 复合数据类型:数组、slice、map、结构体

  • 函数:捕获迭代变量问题、变长参数声明使用…、延迟函数defer在return语句之后执行,并且可以更新函数的结果变量。

  • 宕机恢复:如果recover函数在延迟函数中调用,且包含这个延迟函数的函数发生了宕机,recover会终止宕机并返回宕机值,发生宕机函数正常返回;如果没有发生宕机,recover会返回nil。

  • 方法
    - 声明: func (t type)fname(xx)(xx){}
    - 方法可以绑定到任何类型上,指针和接口类型除外。
    - t type 称为接受者,可以为指针类型,如果为指针类型可以修改接受者的值;在使用方法时,也必须用指针变量来调用。
    - 封装:封装一个对象,必须使用结构体,封装的原理是命名首字母小写,外包不可见;go语言的封装单元是包

  • 接口
    一个接口类型定义了一套方法,如果有个具体类型要实现该接口,那么必须实现接口类型中定义的所有方法。
    一个接口类型的值可以分为两部分:具体的动态类型和这个动态类型的值。
    空的接口值(类型和值都为nil)与仅仅动态类型的值为nil的接口值是不一样的。
    interface{} 空接口,可以接受一切类型的值
    - 类型断言 x.(T) x是一个接口类型的表达式,T是一个类型

  • goroutine和通道(重点)
    - 通道chan:
    发送语句 ch <- x
    接受语句 x= <-ch
    通过make语句可以创建无缓冲通道(参数2为0或者没有参数2)和有缓冲通道
    通过 v, ok := <- ch 可判断通道是否关闭,或者range关键字也可以做到
    ch <- int 只能发送,不允许接受;<-ch int 只能接受,不能发送
    select 多路复用
    sync.WaitGroup 用于回收goroutine,具体使用方法baidu
    - 无缓冲通道
    往无缓冲通道中发送数据将会阻塞,直到另一个routine接受收据;相反如果先接收数据,也会阻塞直到有数据传过来。
    - 有缓冲通道
    缓冲通道满了发送方阻塞,缓冲通道空了接收方阻塞。

  • 并发控制(重点)

    • sync.Mutex 互斥锁
  • go包工具(go mod 及 go path)
    go help

  • 测试文件(需要实践)
    go test 工具扫描*_test.go文件,并生成一个临时的main包来调用他们,最终运行生成结果。
    在*_test.go文件中,三种函数需要特殊对待:1.以Test前缀命名的,功能测试函数;2.以Benchmark开头的,操作性能测试函数;3.以Example开头的,示例函数。

  • 反射
    在fmt.Printf中使用%T可以打印接口的类型

    • reflect.Type
      reflect.TypeOf函数接受任意interface{}参数,并把接口中的动态类型以reflect.Type形式返回
      在fmt.Printf中使用%T可以打印值的类型

    • reflect.Value
      reflect.ValueOf函数接受任意interface{}参数,并把接口的动态值以reflect.Value形式返回
      使用Value的kind的方法可以来区分不同的类型,类型的分类只有如下少数几种:基础bool、string及各种数字类型;聚合类型array、struct;应用类型chan、map、slice、ptr、func;接口类型interface;空值invalid。

< 工作中学习goland的收获 >

goland中获取网络包导入 : go get

单个线程在无缓冲通道上发送信息会被阻塞,而在有缓冲通道上(通道不满)不会阻塞。

关于go语言的反射机制:[反射值对象valueOf()和反射类型typeOf()]
a = reflect.typeOf(value) 获取value的反射类型
a.Name() 获取反射类型的基本类型(已存在的基本类型和type声明的类型)
a.Kind() 获取反射类型的分类类型(存在的分类类型可以查询)
a.Elem() 获取指针所指元素的反射类型
a = reflect.ValueOf(value) 获取a的反射值对象
a.interface().(int)使用断言来获取值

接口拥有动态类型(赋予接口值的类型–相当于子类)和静态类型(编译时的类型 --即接口类型)
typeOf主要是获取接口的动态类型
valueOf主要是获取原始值,如果值是可寻址的将拥有改变原始值的能力

接口值可以分为动态类型和动态值
不确定:使用类型断言来判断切片和map是否能够承受
类型断言的前提是必须已知类型
a.(T) 当T类型满足a的动态类型是成功断言

我的理解:对于基本类型,完全可以使用类型断言来获取接口的原始值,但是对于未知类型的组合类型就必须的使用valueOf来获取原始值。
核心:对于基本类型、slice(不确定怎么获取)、map(可用断言,不太确定)、struct(未知结构体类型用反射,已知用断言)赋予空接口后想要获取原始值,对于已经赋值的空接口,需要获取他的动态类型使用反射。
在go语言程序设计中有所有类型通过valueOf获取值的方法

断言、接口和反射三大模块来解决void*问题(动态数据类型)

//工作代码,理解反射
func (t *FrameTask) OnWriteData(data interface{}) (isFull bool, err error) {
	if data == nil {
		return
	}
	dataType := reflect.TypeOf(data)
	kind := dataType.Kind().String()
	var modelName string
	if kind == "slice" {
		kind = dataType.Elem().Kind().String()
		if kind == "ptr" {
			modelName = dataType.Elem().Elem().Name()
		} else {
			modelName = dataType.Elem().Name()
		}
		commit, exist := dataCommits[modelName]
		if !exist {
			AddNormalCommit(modelName, strings.ToLower(modelName))
			commit, exist = dataCommits[modelName]
	}
		if exist {
			length := reflect.ValueOf(data).Len()
			for i := 0; i < length; i++ {
				isFull, err = commit.AddCommitData(reflect.ValueOf(data).Index(i).Interface())
			}
			results[commit.TaskApi.TypeName] += int64(length)
		} else {
			fmt.Printf("不存在%s类型commit!", modelName)
		}
	} else {
		if kind == "ptr" {
			modelName = dataType.Elem().Name()
		} else {
			modelName = dataType.Name()
		}
		commit, exist := dataCommits[modelName]
		if !exist {
			AddNormalCommit(modelName, strings.ToLower(modelName))
			commit, exist = dataCommits[modelName]
		}
		if exist {
			isFull, err = commit.AddCommitData(data)
			results[commit.TaskApi.TypeName]++
		} else {
			fmt.Printf("不存在%s类型commit!", modelName)
		}
	}
	return
}

go语言的嵌套结构体赋值:

type param_s struct {
	Type   int `json:"type"`
	Params []struct {
		Start  string   `json:"start"`
		End    string   `json:"end"`
		Length int      `json:"length"`
		Offset int      `json:"offset"`
		Ext    []string `json:"ext"`
	} `json:"params"`
	Path []string `json:"path"`
}
ps = param_s{
		Type: 1,
		Params: []struct{
			Start string "json:\"start\""; End string "json:\"end\""; Length int "json:\"length\""; Offset int "json:\"offset\""; Ext []string "json:\"ext\""
		} {
			{Start:"",End:"",Length:0,Offset:0,Ext:[]string{"jpg"}},
		  },
		Path:[]string{"ceshitezhenghuifu.001/jiaqiong"},
	}

注意在对切片赋值时候的方式。
注:go语言在赋值的时候会把复合类型放在{}前。

总结go path和go mod
当使用gopath时,依赖的文件都必须放在设置gopath/src的目录下,并且go get的命令下载依赖也是下载在gopath/src下,不可以指定依赖版本
当使用gomod时,依赖文件的下载路径在pkg/mod下,并且可以指定依赖版本,重要的一点是,拥有go.mod的目录为项目的根目录,在有本地依赖时必须在go.mod中request and replace
两者的主要区别在于对于gopath的依赖(即对于import导入依赖寻址,gomod使用go.mod而gopath使用gopath变量)

go channel的理解:
make(type, size)
当size为0时,为无缓冲通道,向里面写数据会阻塞,直到数据被读走。
当size大于0是,为缓冲通道,通道满时向里面写数据会阻塞。
_,ok := <-a 非阻塞读取

for a := range ch{
} for循环阻塞读取通道,直到通道被关闭

select{
} 如果没有符合的条件,将会阻塞,如果有default分支在不会阻塞

关于gin中间件的理解:

func Logger() gin.HandlerFunc {
	//两种注册函数方式不同点在于这种方式可以在此处初始化一些
	return func(c *gin.Context) {
		// 可以通过上下文对象,设置一些依附在上下文对象里面的键/值数据
		c.Set("example", "12345")
		// 在这里处理请求到达控制器函数之前的逻辑
		// 调用下一个中间件,或者控制器处理函数(路由处理函数),具体得看注册了多少个中间件。
		l("BBBBB")
		time.Sleep(time.Second)
		c.Next()
		time.Sleep(time.Second)
		l("EEEEE")
		// 在这里可以处理请求返回给用户之前的逻辑
		// 例如,查询请求状态吗
	/*	status := c.Writer.Status()
		log.Println(status)*/
	}
}
//顺序是:next之前先Use的先使用,next之后先Use的后使用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值