go语言学习
go语言中不用写分号
Hello World
package main |
前言
在cmd中输入go version可以查看版本号 |
跨平台编译
只需要指定目标操作系统的平台和处理器架构 |
变量声明,go语言中变量必须先声明再使用
var 变量名称 变量类型 |
常量
//常量的定义 |
iota:ota是go语言的常量计数器,只能在常量的表达式中使用。
iota是go语言的常量计数器,只能在常量的表达式中使用。 |
基本数据类型
go语言中不能定义二进制数,需要定义只能强制转换 |
浮点型:
默认go语言中小数都是float64类型的 |
bool型:
bool类型默认值为false |
字符串操作
字符串是以双引号来包裹的,字符以单引号来包裹(单独的字母、汉字、符号表示一个字符) |
转义符
\r 回车符(返回行首) |
多行字符串
Go语言中要定义一个多行字符串时,就必须使用反引号字符: |
strings常用函数
len(str) 求长度 |
byte和rune类型
var a := '中' |
遍历字符串两种方法
// 遍历字符串 |
修改字符串
要修改字符串,需要先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。 |
类型转换
Go语言中只有强制类型转换,没有隐式类型转换。该语法只能在两个类型之间支持相互转换的时候使用。 |
算数运算符
注意: ++(自增)和--(自减)在Go语言中是单独的语句,并不是运算符。 |
切片
切片的定义和初始化
//切片的定义和初始化 |
使用make()函数构造切片
//构建长度为5,容量为10的int型切片,可以用len获取长度,用cap获取容量 |
使用append()函数添加元素
//判断切片是否为空,使用len(slice) == 0 |
使用copy函数赋值切片,避免直接赋值共用一块内存的问题
//用copy函数可以避免这个问题,让两个分别对应两块不同的内存 |
切片删除元素,利用append函数
//本身没有删除元素的函数,可以利用append函数实现 |
判断切片是否为空
// 要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断。 |
切片不能直接比较
// 切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例: |
map
map的定义
//map的定义,map里的元素都是成对的 |
用for range的遍历方法遍历map
for k, v := range scoreMap { |
判断某个键是否存在
value, ok := scoreMap["张三"] //判断是否有key:张三的存在,存在ok为true并将key对应的值传入value中, |
使用delete删除键值对
delete(scoreMap, "娜扎") //删除key为娜扎的键值对 |
在声明的同时填充元素
userInfo := map[string]string{ |
随机数的使用方法
rand.Seed(time.Now().UnixNano()) //初始化随机数种子 |
函数
//指针 |
defer
//在defer归属的函数即将返回时,先被defer的语句最后被执行,最后被defer的语句,最先被执行。 |
闭包
func myFunc(name string) func() { |
闭包实例
//加后缀的函数 |
panir/recover异常处理
//recover()必须搭配defer使用。defer一定要在可能引发panic的语句之前定义。 |
指针
//&取出地址,*根据地址取出地址指向的值 |
new开辟内存
p := new(int) //new开辟内存, 并把对应内存的地址返回 |
自定义类型与取别名
//自定义类型 |
结构体
结构体定义
//结构体定义 |
结构体的初始化
var p1 person |
使用键值初始化
p2 := person{ //注意每一行后面有逗号 |
匿名结构体
var user struct { |
结构体字段的可见性
结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。 |
构造函数
func newPerson(name, city string, age int8) *person { |
方法和接收者
//方法的定义格式如下: |
任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。 |
包
定义包
package 包名 |
可见性 对外可见的成员注意要写注释
如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public)。 |
包的导入
要在代码中引用其他包的内容,需要使用import关键字导入使用的包。具体语法如下: |
自定义包名
在导入包名的时候,我们还可以为导入的包设置别名。通常用于导入的包名太长或者导入的包名冲突的情况。具体语法格式如下: |
匿名导入包
如果只希望导入包,而不使用包内部的数据时,可以使用匿名导入包。具体的格式如下: |
init()初始化函数
init()函数介绍 |
init()函数执行顺序
Go语言包会从main包开始检查其导入的所有包,每个包中又可能导入了其他的包。Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。 |
重写init函数,相当于构造函数
|
接口
接口是一种抽象的类型
在Go语言中接口(interface)是一种类型,一种抽象的类型。 |
接口的定义
每个接口由数个方法组成,接口的定义格式如下: |
示例
package main |
值接收者和指针接收者实现接口的区别
package main |
接口嵌套
接口与接口间可以通过嵌套创造出新的接口。 |
空接口
空接口的定义 |
空接口的应用
空接口作为函数的参数 |
类型断言
想要判断空接口中的值这个时候就可以使用类型断言,其语法格式: |
反射
reflect包
在Go语言的反射机制中,任何接口值都由是一个具体类型和具体类型的值两部分组成的(我们在上一篇接口的博客中有介绍相关概念)。 |
TypeOf
在Go语言中,使用reflect.TypeOf()函数可以获得任意值的类型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。 |
type name和type kind
在反射中关于类型还划分为两种:类型(Type)和种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。 |
ValueOf
reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。 |
通过反射设置变量的值
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。 |
结构体反射
StructField类型 |
编写函数printMethod(s interface{})来遍历打印s包含的方法。
// 给student添加两个方法 Study和Sleep(注意首字母大写) |
并发
goroutine
package main |
GOMAXPROCS
Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。 |
channel类型
定义
channel是一种类型,一种引用类型。声明通道类型的格式如下: |
创建channel
通道是引用类型,通道类型的空值是nil。 |
channel操作
通道有发送(send)、接收(receive)和关闭(close)三种操作。 |
发送
将一个值发送到通道中。 |
接收
从一个通道中接收值。 |
关闭
我们通过调用内置的close函数来关闭通道。 |
无缓冲的通道
无缓冲的通道又称为阻塞的通道。我们来看一下下面的代码: |
有缓冲的通道
解决上面问题的方法还有一种就是使用有缓冲区的通道。我们可以在使用make函数初始化通道的时候为其指定通道的容量,例如: |
for range从通道循环取值
当向通道中发送完数据时,我们可以通过close函数来关闭通道。 |
单向通道
有的时候我们会将通道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中使用通道都会对其进行限制,比如限制通道在函数中只能发送或只能接收。 |
worker pool(goroutine池)
在工作中我们通常会使用可以指定启动的goroutine数量–worker pool模式,控制goroutine的数量,防止goroutine泄漏和暴涨。 |
select多路复用
select的使用类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的s语句。具体格式如下: |
并发安全和锁
有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。 |
互斥锁
互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。 |
读写互斥锁
互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。 |
sync.Once
在编程的很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。 |
sync.Map
Go语言的sync包中提供了一个开箱即用的并发安全版map–sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法。 |
原子操作
原子操作:使用内置的函数对数据进行操作 |
TCP通信
TCP服务端
package main |
TCP客户端
package main |
UDP通信
UDP服务端
package main |
UDP客户端
package main |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 木风可可!