Go入门
变量
var a,b *int 变量声明
bool,string,uint,byte,float,complex
在go语言中所有的东西都是被初始化的 string = nil
不知名类型声明 var level =1,在编译的时候会初始化类型
批量格式定义
go
复制代码
var (
a int
b string
c []float32
)
i:=1 最简单的模式 只能定义在内部
conn,err := net.Dial("tcp","127.0.0.1") 这样也是可以重新定义conn1,err,两个中只要有一个不同就行
不想接受就是可以为匿名变量 conn,_ 下划线就是匿名变量
全局变量的声明必须以var关键字开头,如果想在外部包中使用全局变量的首字母必须大写 就是在main 外部定义 var int global
func sum(a int,b int) int{}
var is bool
go的值比较非常严格
byte 就是char
var mystr01 string var mystr02 [5]byte = [5]byte{1,2,3,4,5}
字符串长度len 中文字符串长度 utf8.RunCountInString(s)
字符串拼接直接用加号就行 另外一种拼接方式
go
复制代码
//节省内存分配 提交效率
var StringBuilder bytes.Buffer
stringBuilder.WriteString(s1)
stringBuilder.WriteString(s2)
直接索引对rune无效 string([]rune(mystr)[6])
对中文进行遍历需要使用range的方式进行遍历
go
复制代码
for _,s:=range str{
}
go
复制代码
//-1 不存在 获取的是索引值 是从0开始的
postion:=strings.Index(value,"s")
类型转换
go语言不存在隐式类型转换所有的转换必须是显示声明的 int(value)强制转化
不同类型转化会发生错误 bool => int 就是会发生错误
golang语言的字符串是不可变的 所以先转化为[]byte(string)[1] = 1然后再去修改其中的一位
常量
const name string = "nihao"
iota 常量生成器 第一个iota会被置为0
指针
%p代表指针的格式化
&取地址 *取内容
创建指针 prt :=new(string) 创建一个字符串的指针
flag是用来执行命令的
堆:用来存放进程中被动态分配的内存段,大小不固定
栈:就是用来存放局部变量 比较快
类型别名
type rune = int32 这个就是别名
type mybool bool 这个时类型定义
有什么差别呢? 在查看类型时候别名返回的就是原来的类型,但是定义返回的是当前定义的类型
关键字
字符串和其他类型的转换
go
复制代码
//str 转int
str := "1"
intv, _ := strconv.Atoi(str)
fmt.Println("%T", intv)
//int 转str
intv1 := 1
strv := strconv.Itoa(intv1)
fmt.Println("%T", strv)
//str to float 字符串到float
strf := "3.1415"
stof, _ := strconv.ParseFloat(strf, 64)
fmt.Println(stof)
//浮点数转化为字符串
strconv.FormatFloat()
控制台标准输入输出
数组
Go语言中的数组的长度是固定的
var arr [10]int
go
复制代码
var arr [10]int
fmt.Println(arr)
for index, value := range arr {
fmt.Println(index, value)
}
arr1 := [2]int{1, 2} //推荐这样声明
arr3 := [...]int{1, 2, 3} //自动推断
fmt.Println(arr1, arr3)
//数组的比较可以对同类型的数组进行比较 只有所有的值和个数相等的时候才能比较
var arrmore [10][10]int
fmt.Println(arrmore)
arrmore1 := [2][2]int{{10, 1}, {1, 5}}
fmt.Println(arrmore1)
//多维数组要是类型相同也是可以相互赋值的 持有的是引用
切片
和数组差不多,不过是长度是不确定的,每个都将数组作为底层数组,切片是引用类型,左闭右开
a[:] a[1:3] a[:3]
声明一个切片 var name []int ,
go
复制代码
//数组的比较可以对同类型的数组进行比较 只有所有的值和个数相等的时候才能比较
var arrmore [10][10]int
fmt.Println(arrmore)
arrmore1 := [2][2]int{{10, 1}, {1, 5}}
fmt.Println(arrmore1)
//多维数组要是类型相同也是可以相互赋值的
var sle []int //生成空切片
l := len(sle)
fmt.Println(l)
sle = append(sle, 1)
//使用make函数生成切片 make([]Type,size,cap) size是为这个元素分配提前分配,cap下次扩充加多少个
mkt := make([]int, 10)
fmt.Println(mkt)
//复制函数 coyp(目标,要复制的) //必须要同类型长度足够 返回值为个数
arrrr := []int{10, 20, 30, 40, 50}
arrrrc := []int{10, 2, 58, 8}
fmt.Println(arrrr, arrrc)
map
go
复制代码
var mymap map[string]int //键值对
mymap2 := map[string]int{"nihao": 1, "buhao": 2}
fmt.Println(mymap, mymap2)
fmt.Println(mymap["nihao"])
//make 创建
mmap := make(map[string]string, 10) //虽然会自动加1 但是最好先初始一下
fmt.Println(mmap)
//一个切片对应多个值
mms := make(map[string][]int)
mms2 := make(map[string]*[]int)
mms["name"] = []int{1, 2, 3}
mms["555"] = []int{5, 2, 3}
fmt.Println(mms, mms2)
for k, v := range mms {
fmt.Println(k, v)
}
//清空map 重新生成一个 线程不安全的
//线程安全map sync.Map
var mysyncmap sync.Map
mysyncmap.Store("key", "value")
mysyncmap.Store("key2", "value2")
mysyncmap.Load("key2")
mysyncmap.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true
})
nil
nil 是不能进行比较 nil==nil
不同的值nil对应的大小是不一样的
new 和make
if else
go
复制代码
if 1 == 1 {
fmt.Println("true")
}
//特殊写法
if x:=100;x>10 {
fmt.Println("x")
//这个x是块级作用域
}
for
go
复制代码
for i := 0; i < 20; i++ {
fmt.Println(i)
}
//死循环
for{
break
}
//直接写条件相当于while
for a>100{
goto bTag
}
//标签
break bTag:
for range
go
复制代码
forragnge := make(map[string]string)
forragnge["a"] = "a"
forragnge["b"] = "b"
for i, v := range forragnge {
fmt.Println(i, v)
}
switch
go
复制代码
a := 1
switch a {
case 1:
fmt.Println("1")
case 2, 3, 4:
fmt.Println("2,3,4")
}
//LIANG
switch {
case a==2:
fmt.Println("a==2")
fallthrough //这个就是可以往下走 无论成功与否
case a==3:
fmt.Println("a==3")
}
goto
标签 name:
使用 goto name
使用goto处理错误
break continue
break 也可以跳出一个代码块 也可以和goto一样使用
continue 就是跳过本次 也可以和goto一样使用
函数
go
复制代码
func max(n1,n2 int32) (int32,string) {
if n1 > n2 {
return n1
}
return n2,"nihao"
}
func funarg(fn func() int) int32{
return int32(fn())
}
go
复制代码
func funcr() (ni string, hao string) {
ni = "hello"
hao = "world"
return //对自动返回名字
}
参数
map,slice,chan,指针,interface默认是引用的方式进行传递
多个参数 args 本质上就是一个切片
返回一个闭包 那么里面的变量的值都是被持有的 那么里面的值每一个次都是不会被清空的
延迟调用 defer
defer 不能直接执行 要放到匿名函数中 不然就是会出现问题
defer 在闭包内使用到的变量 会保存上下文用到的变量
错误处理
panic 遇到panic() ,就会终止 然后执行defer语句 再报告异常信息 然后推出gorotine 如果再defer中使用了revocer()函数,就会捕获醋无哦信息,是该错误信息终止报告
recover() 可以捕获到异常
延时调用中的错误,可以被后续的延时调用捕获,单只可以捕获最后一个
go
复制代码
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("error!")
结构体
go
复制代码
type my_struct struct {
name string
id int
x int
y int
}
var mys1 my_struct
mys1.x = 1
mys1.y = 2
mys := my_struct{id: 1, name: "nihao", x: 1, y: 2}
fmt.Println(mys)
//使用new进行实例化
ins := new(my_struct)
ins.id = 1
类
go
复制代码
type bag struct {
name string
goods []string
}
func (b *bag) GetGoods() string {//命名建议使用首字符
return b.goods[0]
}
go
复制代码
type bag struct {
name string
}
type Big struct {
bag
}
//组合可以直接调用 bag中的方法等
bb := Big{bag{"11"}}
接口
go
复制代码
type AnimalIF interface {
Sleep()
GetName() string
SetName(string)
}
当你的struct去实现了接口就是实现了所有的方法 就相当于你实现了这个接口 这个就是非侵入式
一个类型可以实现多个接口
空接口 var int interface{}
断言
go
复制代码
v, ok := ink.(int) //类型断言
fmt.Println(v, ok) //第一个参数值,第二个参数状态
I/O操作
Reader
go
复制代码
reader := strings.NewReader("askjdfk sadfkjsad fasdfjk sdf")
p := make([]byte, 8)
for {
n, err := reader.Read(p)
if err == io.EOF {
fmt.Println("读完了",n)
break
}
fmt.Println(p[:n])
}
文件操作api
go
复制代码
//创建文件写入
create, _ := os.Create("test.txt")
n, err := create.Write([]byte("hello world"))
if err != nil {
return
}
fmt.Println(n)
create.Close()
//读取文件写入
var rr [32]byte
rr := make([]byte, 32)
open, _ := os.Open("test.txt")
open.Read(rr) //这里面要求传入的是切片 注意注意!!!!
fmt.Println(rr)
//文件删除
os.Remove("test.txt")
bufio 带缓冲区的文件读写
go
复制代码
file, _ := os.OpenFile("test.txt", os.O_APPEND, 0777)
writer := bufio.NewWriter(file)
for i := 0; i < 10; i++ {
builder := strings.Builder{}
builder.WriteString("hello")
builder.WriteString(strconv.Itoa(i))
builder.WriteString("\n\r")
s := builder.String()
writer.WriteString(s)
}
writer.Flush()
reader:= bufio.NewReader(file)
for{
line, _, err := reader.ReadLine()
}
ioutil
go
复制代码
//读取文件所有的内容
file, err := os.ReadFile("test.txt")
if err != nil {
}
fmt.Println(string(file))
//讲内容完全写入文件
os.WriteFile("test2.txt", []byte("sdafasdf"), 0777)
readFile, err := os.ReadFile("test2.txt")
if err != nil {
}
fmt.Println(string(readFile))
goroutine
go
复制代码
func test(){
fmt.Println("test")
}
func main(){
go test()//这个携程是依赖于main goroutine
fmt.Println("main groutine")
}
runtime包
runtime.Gosched() 再次重新分配一下任务
runtime.Goexit() 退出携程
runtime.GOMAXPROCES(1) 设置核心数
channel
var ch chan int 创建一个int型的通道
声明完成需要make才能使用 make(chan 元素类型,[缓冲区大小])
ch:=make(chan int,10)
操作 :发送,接受,关闭
发送和接受使用 <- 这个符号
发送 ch<-10 把10发送金桔
接受 x:=<-ch
关闭通道 close(ch) ,通道可以被垃圾回收机制给回收,对一个关闭的通道发送数据就会导致panic
无缓冲的channel
无缓冲的通道必须要存在接受者才能发送 无缓冲会阻塞
go
复制代码
func rec(ch chan int){
temp:=<-chan
fmt.PrintLn(temp)
}
func main(){
ch:=make(chan int)
rec(ch)
ch<-10
fmt.PrintLn("发送者")
}
go
复制代码
func main(){
ch:=make(chan int,1)
ch<-1 //可以先放入一个
}
如何判断通道已经被关闭了 使用for range 就可以判断channel是否关闭 关闭了for range就是退出循环了
单向通道
go
复制代码
var dan chan<- int = make(chan int)
func counter(out chan<- int){
//这个代表的意思是只能往里面去放入
out<-10
}
func chu(in <-chan int){
//这个就是只能出
temp:=<-in
}
select 可以同时相应多个通道的操作
select 反正是看谁先
go
复制代码
select{
case <-chan1:
//如果从chan1读取到数据
case chan1<-:
//如果成功写入数据
default:
//都没有成功
}
go
复制代码
func test(ch chan int){
select{
case ch<- 1:
fmt.p("写")
default:
//到这边执行就是满了
}
}
并发安全和锁
互斥锁 var lock sync.Mutex
lock.Lock() lock.Unlock() 同一时间只能够保证有一个进入临界区
读写互斥锁 当一个goroutine获得读锁 其他来也可以获得读 不可以获得写 当加上一个写锁 读和写都无法执行 sync.RWMutex
加锁涉及到内核态的转变 消耗比较高
基本数据类型 可以使用原子操作 sync/atomic atomic
Golang 调度器
Go的协程
g携程 只占几kb
m线程
p管理本地的队列
M线程通过P 去操作G ,p会认为是cpu操作的核心数,GOMAXPROCES个数
线程想要运行就要先从p去获取携程
调度策略
网络编程
socket
就是封装了tcp/ip协议 只要调用socket就可以轻松实现tcp ip编程
TCP编程
面向连接,可靠的,基于字节流的通讯协议,因为是面向连接的,数据像流水一样传输,会存在黏包问题
使用net包 net.Listen("tcp","127.0.0.1:20000")
net包也可以作为服务器的客户端 发起一个连接
UDP编程
实时性比较好
net.ListenUDP("udp",&net.UDPAddr{
IP:net.IPv4(0,0,0,0)//监听所有
Port:3000,
})
HTTP编程
websocket
MYSQL
go
复制代码
func init(){
//在执行main方法前会执行init方法
sql.Open("mysql","root:root@tcp(localhost:3306)/go_table")
DB.Exec("insert into user() values(?,?,?)",username,sex,e)
rows,err = DB.Query("select * form user where id = ?",id)
for rows.Next(){
}
//事务
con,err:=DB.Begin()
_ := Db.commit()
_ := Db.rollback()
}
redis
Go泛型
go
复制代码
func IntOrFloats[K comparable,V int64|float64](m map[K]V)V{
//前面再中括号中去定义类型,然后后面使用, 使用前面的名字都行,返回值和参数都可以是泛型 里面的变量声明也可以使用KV去代替为泛型
}
//泛型的定义
type fanxing interface{//定义一个类型约束
int | float64 |string
}
//这边去调用泛型
func IntOrFloats[K comparable,V fanxing](m map[K]V)V{
//前面再中括号中去定义类型,然后后面使用, 使用前面的名字都行,返回值和参数都可以是泛型 里面的变量声明也可以使用KV去代替为泛型
}
多模块工作目录
再包含两个项目的大目录下 go work init ./fly-falsh 随便那个都行
go work use ./common 两个都加入
模糊测试