Go入门
变量
var a,b *int 变量声明
bool,string,uint,byte,float,complex
在go语言中所有的东西都是被初始化的 string = nil
不知名类型声明 var level =1,在编译的时候会初始化类型
批量格式定义
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)
字符串拼接直接用加号就行 另外一种拼接方式
//节省内存分配 提交效率
var StringBuilder bytes.Buffer
stringBuilder.WriteString(s1)
stringBuilder.WriteString(s2)
直接索引对rune无效 string(\[\]rune(mystr)6 )
对中文进行遍历需要使用range的方式进行遍历
for _,s:=range str{
}
//-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 这个时类型定义
有什么差别呢? 在查看类型时候别名返回的就是原来的类型,但是定义返回的是当前定义的类型
关键字
字符串和其他类型的转换
//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
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: a1:3 a:3
声明一个切片 var name \[\]int ,
//数组的比较可以对同类型的数组进行比较 只有所有的值和个数相等的时候才能比较
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
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
if 1 == 1 {
fmt.Println("true")
}
//特殊写法
if x:=100;x>10 {
fmt.Println("x")
//这个x是块级作用域
}
for
for i := 0; i < 20; i++ {
fmt.Println(i)
}
//死循环
for{
break
}
//直接写条件相当于while
for a>100{
goto bTag
}
//标签
break bTag:
for range
forragnge := make(map[string]string)
forragnge["a"] = "a"
forragnge["b"] = "b"
for i, v := range forragnge {
fmt.Println(i, v)
}
switch
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一样使用
函数
func max(n1,n2 int32) (int32,string) {
if n1 > n2 {
return n1
}
return n2,"nihao"
}
func funarg(fn func() int) int32{
return int32(fn())
}
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() 可以捕获到异常
延时调用中的错误,可以被后续的延时调用捕获,单只可以捕获最后一个
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("error!")
结构体
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
类
type bag struct {
name string
goods []string
}
func (b *bag) GetGoods() string {//命名建议使用首字符
return b.goods[0]
}
type bag struct {
name string
}
type Big struct {
bag
}
//组合可以直接调用 bag中的方法等
bb := Big{bag{"11"}}
接口
type AnimalIF interface {
Sleep()
GetName() string
SetName(string)
}
当你的struct去实现了接口就是实现了所有的方法 就相当于你实现了这个接口 这个就是非侵入式
一个类型可以实现多个接口
空接口 var int interface{}
断言
v, ok := ink.(int) //类型断言
fmt.Println(v, ok) //第一个参数值,第二个参数状态
I/O操作
Reader
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
//创建文件写入
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 带缓冲区的文件读写
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
//读取文件所有的内容
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
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
无缓冲的通道必须要存在接受者才能发送 无缓冲会阻塞
func rec(ch chan int){
temp:=<-chan
fmt.PrintLn(temp)
}
func main(){
ch:=make(chan int)
rec(ch)
ch<-10
fmt.PrintLn("发送者")
}
func main(){
ch:=make(chan int,1)
ch<-1 //可以先放入一个
}
如何判断通道已经被关闭了 使用for range 就可以判断channel是否关闭 关闭了for range就是退出循环了
单向通道
var dan chan<- int = make(chan int)
func counter(out chan<- int){
//这个代表的意思是只能往里面去放入
out<-10
}
func chu(in <-chan int){
//这个就是只能出
temp:=<-in
}
select 可以同时相应多个通道的操作
select 反正是看谁先
select{
case <-chan1:
//如果从chan1读取到数据
case chan1<-:
//如果成功写入数据
default:
//都没有成功
}
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
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泛型
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 两个都加入
模糊测试