go note
Posted eiguleo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go note相关的知识,希望对你有一定的参考价值。
Go note
1,Go语言中,空接口可以指向任何数据对象,所以可以使用interface{}定义任意类型变量,同时interface{}也是类型安全的。
2,
x, y := func(i, j int) (m, n int) { // x y 为函数返回值
return j, i
}(1, 9) // 直接创建匿名函数并执行
3,sub can assignment value to base type
4,panic recovery
recover()用于将panic的信息捕捉。recover必须定义在panic之前的defer语句中。在这种情况下,当panic被触发时,该goroutine不会简单的终止,而是会执行在它之前定义的defer语句
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type // 出现在任意位置
vname1, vname2, vname3 = v1, v2, v3 //出现在任意位置
var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 //出现在:=左侧的变量不应该是已经被声明过的,且只能在函数体内出现
#常量
const MAX = 4096
const LIMIT int16 = 1024
const LIMIT2 = int16(1024)
const (
start = 0x1
resume = 0x2
stop = 0x4
)
#预定义常量
const(
c0 = iota //0
c1 = iota //1
c2 = iota //2
)
const(
a= iota //a==1
b //b=2
c //c=4
)
#枚举
const (
Sunday=iota
Monday
Tuesday
number //不能被为包私有,其他包不能访问
)
浮点数比较用安abs(a-b)<p的形式
#字符串
var str string
string="hello"
ch :=str[0]
字符串内容不能修改
字符串操作x+y len(s) s[i]
for i:=0;i<n;i++ {ch= s[i]} //字节数组遍历
for i,ch := range string {} //以Unicode字符遍历
#字符类型 byte 和rune(Unicode)
#go支持utf-8
#数组
长度不能更改
var x [21]byte//[2*N] struct {x,y int32}//[1000]*
float64//[3][5]int二维数组
访问 for i:=0;i<n;i++{}//for i,v := range arry {}
数组是值类型
#数组切片slice
抽象为3个变量 一个指向原生数组的指针 数组切片中的元素个数 数组切片已分配的存储空间
创建
当传参时,函数接收到的参数是数组切片的一个复制,虽然两个是不同的变量,但是它们都有一个指向同一个地址空间的array指针,当修改一个数组切片时,另外一个也会改变,所以数组切片看起来是引用传递,其实是值传递。新旧两个切片,首地址相同
当你重新划分一个slice时,新的slice将引用原有slice的数组。如果你忘了这个行为的话,在你的应用分配大量临时的slice用于创建新的slice来引用原有数据的一小部分时,会导致难以预期的内存使用。
在某些情况下,在一个slice中添加新的数据,在原有数组无法保持更多新的数据时,将导致分配一个新的数组。而现在其他的slice还指向老的数组(和老的数据)。
slice的名字占据的就是slice指向数组的指针,传递给函数的时候传递的也是这个地址
var arr [10]int = [10]int{1,2,3,4,5,6,7,8,9,0}
var sli := arr[:5] //基于数组创建,也可以基于切片创建
sli:= make([]int,5,10)//直接创建初始元素为5,初始值为0,预留10空间(可省略)
sli:=[]int{1,2,3,4,5}//直接创建
遍历
for i:=0;xxxx //for i,v := range(sli)
增减元素
append(sli, 1,2,3)// 也可以增加切片
support cap() and len()copy(sl1,sl2) //copy 覆盖位置sli1, 覆盖范围以最小长度切片长度为准
#map集合 无序,引用类型
var map_name map[key_type] elment_type
创建
map_name=map[string] PersionInfo{"1","jack","Room101"} 创建初始化
集合不可以在main初始化并赋值,然后在main中使用,可以在main创建,在main后增加kv并使用
数组,变量,slicen可以,猜测原因是map是动态的,main前属于静态分区
不要使用 new,永远用 make 来构造 map,如果你错误的使用 new() 分配了一个引用对象,你会获得一个空引用的指针,相当于声明了一个未初始化的变量并且取了它的地址
package main
import "fmt"
var map1 map[string]int = make(map[string]int)
/*put here will report error
map1["a"] = 3
map1["b"] = 2
/*
func a(x interface{}) {
fmt.Println(x)
}
func main() {
/*put here ok
map1["a"] = 3
map1["b"] = 2
/*
a(map1)
}
#range
range 非引用类型时,恢复至一份副本,不会改变遍历对象
range引用类型时,复制引用类型的地址,会改变遍历对象
在函数中创建一个channel返回,同时创建一个goroutine往channel中塞数据”这是一个重要的惯用法
序列发生器、fan-out、fan-in
range可以迭代的数据类型包括array,slice,map,string和channel。在这些数据类型里面只有array类型能以指针的形式出现在range表达式中
2.golang中大多数是传值的,有://存疑
基本类型:byte,int,bool,string
复合类型:数组,数组切片,结构体,map,channne
#panic recover 需要在defer中,panic必须在recover的外第一层
func main(){
defer func(){
fmt.Println(recover())
}
panic("not good")
}
https://www.cnblogs.com/sunsky303/p/7729165.html#%E5%9C%A8Slice%E4%B8%AD
https://studygolang.com/articles/371
#字符串不能赋值为nil, "" is right
Strings无法修改
你可以在map创建时指定它的容量,但你无法在map上使用cap()函数。
在多行的Slice、Array和Map语句中遗漏逗号是错误的
以小写字母开头的结构体将不会被(json、xml、gob等)编码,因此当你编码这些未导出的结构体时,你将会得到零值。
向无缓存的Channel发送消息,只要目标接收者准备好就会立即返回
向已关闭的Channel发送会引起Panic
在一个nil的channel上发送和接收操作会被永久阻塞
如果结构体中Structs, Arrays, Slices, and Maps的各个元素都可以用你可以使用等号来比较的话,那就可以使用相号, ==,来比较结构体变量。
当你通过把一个现有(非interface)的类型定义为一个新的类型时,新的类型不会继承现有类型的方法。如果你确实需要原有类型的方法,你可以定义一个新的struct类型,用匿名方式把原有类型嵌入其中。
interface类型的声明也会保留它们的方法集合。
for _,v := range data {
go func() {
fmt.Println(v)
}() //err
}
for _,v := range data {
go func(xxx) {
fmt.Println(v)
}(xxxx)
} //ok
被defer的函数的参数会在defer声明时求值(而不是在函数实际执行时)。
被defer的调用会在包含的函数的末尾执行,而不是包含代码块的末尾(也就是for block中不执行)。对于Go新手而言,一个很常犯的错误就是无法区分被defer的代码执行规则和变量作用规则。如果你有一个长时运行的函数,而函数内有一个for循环试图在每次迭代时都defer资源清理调用,那就会出现问题。
for _,target := range targets {
f, err := os.Open(target)
if err != nil {
fmt.Println("bad target:",target,"error:",err) //prints error: too many open files
break
}
defer f.Close() //will not be closed at the end of this code block
//do something with the file...
}
解决这个问题的一个方法是把代码块写成一个函数。
for xx:=range a{ defer fun(){print xxx} }
defer可以读取有名返回值,return后继续修改返回值
func c() (i int) { //i=2
defer func() { i++ }()
return 1
}
#函数
命名返回参数,return语句可以为空。return 不为空,返回值顺序是return的顺序而非在函数头声明的顺序
在Go语言中,函数也是一种变量,可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值
type testInt func(int) bool //声明了一个函数类型
func filter(slice []int, f testInt) []int { xxx}
func isOdd(integer int) bool {xxx}
filter(a, isOdd) //这种用法,在写接口的时候非常有用
可以定义函数类型,也可以将函数作为值进行传递(默认值nil)
//定义函数类型callback
type callback func(s string)
//定义一个函数,可以接收另一个函数作为参数
// sum为参数名称, func(int, int) int为参数类型
func test(a, b int, sum func(int, int) int) {
println( sum(a,b) )
}
func main(){
//演示1
var cb callback
cb = func(s string) {
println(s)
}
cb("hello world")
//演示2
test(1, 2, func(a, b int) int {return a + b})
}
参数传递:可变参数
变参本质上就是一个slice,且必须是最后一个形参
将slice传递给变参函数时,注意用…展开,否则会被当做dang单个参数处理,和python类似
func sum(s string, args ...int) {
var x int
for _, n := range args {
x += n
}
println(s, x)
}
sum("0+1+2+3=", x[:4]...)
传指针多个函数能操作同一个对象
传指针比较轻量级(8byte),只是传内存地址,我饿们可以用指针来传递体积大的结构体
Go语言中,string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传指针
注意,若函数需要改变 slice长度,仍需要取地址传指针
关键字func声明
可以有一个或多个参数,每个参数后面带有类型,通过","分隔函数可以返回多个值
返回值声明,可以只声明类型
如果没有返回值,可以省略最后的返回信息
如果有返回值,必须在外层添加return
函数使用func开头,左大括号不能另起一行
小写字母开头的函数指在本包内可见,大写字母开头的函数才能被其他包调用
当函数有返回值时,如果返回值没有定义变量,那么一定要使用return加上返回值退出函数。
// getMsg函数需要返回一个string类型值
func getMsg() string {
return "hello"
}
//返回hello
func getMsg() (msg string) {
msg = "world"
return "hello"
}
//返回world
func getMsg() (msg string) {
msg = "world"
return
}
匿名函数
f := func(x,y int) int {
return x + y
}
#接口
使用步骤先定义
1,定义接口,包括方法
2,在某类型中增加该方法
3,将类型变量赋值给已初始化接口变量
4,接口变量调用该方法
go语言可以根据下面的函数:
func (a Integer) Less(b Integer) bool
自动生成一个新的Less()方法//反之不行
func (a *Integer) Less(b Integer) bool
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
当我们不关心某个类型是什么而只是关心他有什么样的行为
给一个struct增加方法的样例,也是面向对象
type mp3 struct { name string }
func (m mp3) play() { fmt.Println(" is playing mp3")}
file := "let‘s go.mp3"
mu := mp3{file}
mu.play()
// 薪资计算器接口
type SalaryCalculator interface { CalculateSalary() int}
// 普通挖掘机员工
type Contract struct { empId int basicpay int}
// 有蓝翔技校证的员工
type Permanent struct { empId int basicpay int jj int // 奖金}
//给类型实现方法
func (p Permanent) CalculateSalary() int {
return p.basicpay + p.jj
}
func (c Contract) CalculateSalary() int {
return c.basicpay
// 总开支
func totalExpense(s []SalaryCalculator) {
expense := 0
for _, v := range s {
expense = expense + v.CalculateSalary()
}
fmt.Printf("总开支 $%d", expense)
}
func main() {
pemp1 := Permanent{1,3000,10000}
pemp2 := Permanent{2, 3000, 20000}
cemp1 := Contract{3, 3000}
employees := []SalaryCalculator{pemp1, pemp2, cemp1}
totalExpense(employees)
}
接口内部实现
package main
import (
"fmt"
)
type Test interface {
Tester()
}
type MyFloat float64
func (m MyFloat) Tester() {
fmt.Println(m)
}
func describe(t Test) {
fmt.Printf("Interface 类型 %T , 值: %v
", t, t)
}
func main() {
var t Test
f := MyFloat(89.7)
t = f
describe(t)
t.Tester()
}
//i.(T) 打印类型
package main
import (
"fmt"
)
func assert(i interface{}) {
v, ok := i.(int) //i.int
fmt.Println(v, ok)
}
func main() {
var s interface{} = 56
assert(s)
var i interface{} = "Steven Paul"
assert(i)
}
golang中x.(type)只能在switch中使用
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
父子接口
package main
import ( "fmt" )
type R interface { Read() }
type W interface { Write(name string)}
type RW interface { R W}
type log struct { name []string r int}
func (t *log) Read() {
if len(t.name) > t.r {
fmt.Println(t.name[t.r])
t.r++
} else {
fmt.Println("empty")
}
}
func (t *log) Write(name string) {
t.name = append(t.name, name)
fmt.Println("wirte success.", t.name)
}
func main() {
//全集可以赋值给子集,结构体赋值给接口
var r R = &log{}
var w W = &log{}
w.Write("write first")
w.Write("write second")
r.Read()
r.Read()
//判断w 是否实现RW 接口
_, ok := w.(RW)
fmt.Println(ok)
}
#struct
package main
import "fmt"
type Books struct {
title string
}
func main() {
//----init 1
var Book1 Books
Book1.title = "go language"
//-----init 2
bk2 := &Books{"c language"}
fmt.Println(bk2.title)
//-------
printBook1(Book1)
printBook2(&Book1)
}
func printBook1(book Books) {
fmt.Printf("Book title : %s
", book.title)
}
func printBook2(book *Books) {
fmt.Printf("2Book title : %s
", book.title)
}
函数中修改struct,需要传指针
What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.
Now your life, life in the future to play you, now do not work hard, the future suck.
现在不玩命,将来命玩你,现在不努力,未来不给力。
#信道
make 建立信道
var channel chan int = make(chan int)
// 或
channel := make(chan int)
默认的,信道的存消息和取消息都是阻塞的 (叫做无缓冲的信道,不过缓冲这个概念稍后了解,先说阻塞的问题)。也就是说, 无缓冲的信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。
var ch chan int = make(chan int)
func foo() {
ch <- 0 // 向ch中加数据,如果没有其他goroutine来取走这个数据,那么挂起foo, 直到main函数把0这个数据拿走
}
func main() {
go foo()
<- ch // 从ch取数据,如果ch中还没放数据,那就挂起main线,直到foo函数中放数据为止
}
非缓冲信道上如果发生了流入无流出,或者流出无流入,也就导致了死锁
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
//close(ch) 可以显示close
for v := range ch {
fmt.Println(v)
}
} 或者在for中判断 len(ch)<0,break
error ,no close 原因是range不等到信道关闭是不会结束读取的
ch <- 1,put into ch <- ch read out ch
判断close case v,ok := <- c
所有的代码应该符合最小权限
以上是关于go note的主要内容,如果未能解决你的问题,请参考以下文章