导图社区 Golang基础语法
Golang基础语法思维导图,分享给大家,自学写的不好, 多多指正!
编辑于2020-11-09 13:39:05Golang
顺序编程
变量
变量声明
Go语言引入了关键字 var
var v1 string
var v2 int
变量初始化
var v1 string
var v1 = "string"
v1 := "string"
不适合生命全局变量, 只能在作用域内声明局部变量
变量赋值
特点: Go语言支持多重赋值 (x, y = y, x) 其他语言就需要引用第三方变量
匿名变量
由于Go语言的语法检查严格, 不允许出现变量声明但未使用的情况, 所以Go语言为了避免其他语言出现的不得不声明一些没用的变量, 产生运行时内存的浪费, 所以Go语言出现了匿名变量.
例如: 一个函数出现多返回值的时候, 如果有其中一个返回值不会被使用的情况下, 可以用 _ (下划线) 为匿名变量接收.
常量
字面量常量
Go语言中使用了const关键字
const v1,v2 string
常量定义
预定义常量
true
false
iota
const ( c0 = iota c1 c2 )
iota 默认从0开始
const ( v1 string v2 int )
枚举
类型
布尔类型bool
true
false (default)
整型
int8
字节长度
1
int16
字节长度
2
int32
字节长度
4
int 64
字节长度
8
uint8
字节长度
1
即(byte)
uint16
字节长度
2
uint32
字节长度
4
uint64
字节长度
8
int/uint 取决于编译环境
浮点型
float32
float64
复数类型
complex64
字符串
string
子主题
字符类型
数组
数组切片
map
流程控制
条件语句
选择语句
循环语句
跳转语句
函数
函数定义
函数调用
不定参数
多返回值
匿名函数和闭包
错误处理
恐慌panic
严重错误,以杀死程序的方式提示调用者[ 正确的编码规范 ]
panic(value error)
调用一个可能恐慌的API, 要注意处理可能发生的恐慌
在return之前defer任务中处理恐慌

错误error
严重程度不及panic的异常
返回错误或结果
error接口
type error interface{}
errors.New(str) 返回 &errorString
自定义异常

实现系统的error接口
将需要的提示数据封装在实现类中
创建实例的指针,作为error实例返回
code
package main import ( "fmt" ) const ( Male = iota Female Bisexual ) type Gender int func (g Gender)String() string { return []string{"Male","Female","Bisexual"}[g] } type MyError struct { Info string } func (myErr *MyError) Error() string { return myErr.Info } type Human struct { name string age int height int weight int looking int TargetLooking int Rmb int Sex Gender TargetSex Gender } func (h *Human)Marry(o *Human) (happiness int, err error) { if h.TargetSex != o.Sex { panic(&MyError{"性取向异常"}) } // 返回错误 if o.looking < h.TargetLooking { err = &MyError{"颜值过低"} //err.Error() return } happiness = o.height*o.looking*o.Rmb return } func NewHuman(name string, age, height,weight,looking,TargetLooking,Rmb int, Sex, TargetSex Gender) *Human { human := new(Human) human.weight = weight human.looking = looking human.height = height human.TargetLooking = TargetLooking human.Sex = Sex human.Rmb = Rmb human.TargetSex = TargetSex human.age = age return human } func main() { defer func() { if err := recover(); err != nil{ fmt.Println(err) } }() kuke := NewHuman("库克",0,180,150,40,90,999999999,Female,Male) nimei := NewHuman("你妹",25,170,100,0,0,999999999,Female,Male) happiness,err := kuke.Marry(nimei) fmt.Println(happiness,err) }
pointer
定义
一个指针变量指向了一个值的内存空间
指针数组
定义一个指针数组来存储地址
指针的指针
指向指针的指针
指针参数
引用或地址传参,在函数调用时可改变其值
Struct
定义
结构体定义需要使用type和struct语句。struct语句定义一个新的数据类型,结构体中有一个或多个成员。type语句设定了结构体的名称
访问成员
结构体 . 成员
结构体指针
var struct_pointer *Book
Slice
定义切片
var v_name []byte
初始化
s := []int{1,2,3}
空切片
一个切片在未初始化之前默认值为nil,长度为0
切片截取
可以通过设置下线及上线来设置截取切片[lower-bound : upper : bound]
切片函数
append()和copy()函数
interface
defind
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。( 方法的集合 )
什么是实现了接口呢? 可以参考鸭子模型
An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.
Example
package main import ( "fmt" "math" ) type geometry interface { area() float64 perim() float64 } type rect struct { width, height float64 } type circle struct { radius float64 } func (r rect) area() float64 { return r.width * r.height } func (r rect) perim() float64 { return 2*r.width + 2*r.height } func (c circle) area() float64 { return math.Pi * c.radius * c.radius } func (c circle) perim() float64 { return 2 * math.Pi * c.radius } func measure(g geometry) { fmt.Println(g) fmt.Println(g.area()) fmt.Println(g.perim()) } func main() { r := rect{width: 3, height: 4} c := circle{radius: 5} measure(r) measure(c) } $ go run interfaces.go {3 4} 12 14 {5} 78.53981633974483 31.41592653589793
接口继承
An interface T may use a (possibly qualified) interface type name E in place of a method specification. This is called embedding interface E in T. The method set of T is the union of the method sets of T’s explicitly declared methods and of T’s embedded interfaces.
Example
type Reader interface { Read(p []byte) (n int, err error) Close() error } type Writer interface { Write(p []byte) (n int, err error) Close() error } // ReadWriter's methods are Read, Write, and Close. type ReadWriter interface { Reader // includes methods of Reader in ReadWriter's method set Writer // includes methods of Writer in ReadWriter's method set }
Duck Typing
鸭子模型 那什么鸭子模型? 鸭子模型的解释,通常会用了一个非常有趣的例子,一个东西究竟是不是鸭子,取决于它的能力。游泳起来像鸭子、叫起来也像鸭子,那么就可以是鸭子。 动态语言,比如 Python 和 Javascript 天然支持这种特性,不过相对于静态语言,动态语言的类型缺乏了必要的类型检查。 Go 接口设计和鸭子模型有密切关系,但又和动态语言的鸭子模型有所区别,在编译时,即可实现必要的类型检查。 什么是 Go 接口 Go 接口是一组方法的集合,可以理解为抽象的类型。它提供了一种非侵入式的接口。任何类型,只要实现了该接口中方法集,那么就属于这个类型。 举个例子,假设定义一个鸭子的接口。如下: type Duck interface { Quack() // 鸭子叫 DuckGo() // 鸭子走 } 假设现在有一个鸡类型,结构如下: type Chicken struct { } func (c Chicken) IsChicken() bool { fmt.Println("我是小鸡") } 这只鸡和一般的小鸡不一样,它比较聪明,也可以做鸭子能做的事情。 func (c Chicken) Quack() { fmt.Println("嘎嘎") } func (c Chicken) DuckGo() { fmt.Println("大摇大摆的走") } 注意,这里只是实现了 Duck 接口方法,并没有将鸡类型和鸭子接口显式绑定。这是一种非侵入式的设计。 我们定义一个函数,负责执行鸭子能做的事情。 func DoDuck(d Duck) { d.Quack() d.DuckGo() } 因为小鸡实现了鸭子的所有方法,所以小鸡也是鸭。那么在 main 函数中就可以这么写了。 func main() { c := Chicken{} DoDuck(c) } 执行正常。如此是不是很类似于其他语言的多态,其实这就是 Go 多态的实现方法。 空接口 继续说说空 interface。 如果一个 interface 中如果没有定义任何方法,即为空 interface,表示为 interface{}。如此一来,任何类型就都能满足它,这也是为什么当函数参数类型为 interface{} 时,可以给它传任意类型的参数。 示例代码,如下: package main import "fmt" func main() { var i interface{} = 1 fmt.Println(i) } 更常用的场景,Go 的 interface{} 常常会被作为函数的参数传递,用以帮助我们实现其他语言中的泛型效果。Go 中暂时不支持 泛型,不过 Go 2 的方案中似乎将支持泛型。 总结 回答结束,做个简单总结。理解 Go 接口要记住一点,接口是一组方法的集合,这句话非常重要,理解了这句话,再去理解 Go 的其他知识,比如类型、多态、空接口、反射、类型检查与断言等就会容易很多。
错误示范
接口不能自己继承自己
不可以两个接口互相继承
illegal: Bad cannot embed itself type Bad interface { Bad } illegal: Bad1 cannot embed itself using Bad2 type Bad1 interface { Bad2 } type Bad2 interface { Bad1 }
Coroutine
WebServer几种主流的并发模型
多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求;但在高并发环境下,多线程的开销比较大;
基于回调的异步IO,如Nginx服务器使用的epoll模型,这种模式通过事件驱动的方式使用异步IO,使服务器持续运转,但人的思维模式是串行的,大量回调函数会把流程分割,对于问题本身的反应不够自然;
协程,不需要抢占式调度,可以有效提高线程的任务并发性,而避免多线程的缺点;但原生支持协程的语言还很少。
协程(coroutine)是Go语言中的轻量级线程实现,由Go运行时(runtime)管理。 在一个函数调用前加上go关键字,这次调用就会在一个新的goroutine中并发执行。当被调用的函数返回时,这个goroutine也自动结束。需要注意的是,如果这个函数有返回值,那么这个返回值会被丢弃。
通讯模型
共享内存
来看下面的例子,10个goroutine共享了变量counter,每个goroutine执行完成后,将counter值加1.因为10个goroutine是并发执行的,所以我们还引入了锁,也就是代码中的lock变量。在main()函数中,使用for循环来不断检查counter值,当其值达到10时,说明所有goroutine都执行完毕了,这时main()返回,程序退出。 package main import ( "fmt" "sync" "runtime" ) var counter int = 0 func Count(lock *sync.Mutex) { lock.Lock() counter++ fmt.Println("counter =", counter) lock.Unlock() } func main() { lock := &sync.Mutex{} for i:=0; i<10; i++ { go Count(lock) } for { lock.Lock() c := counter lock.Unlock() runtime.Gosched() // 出让时间片 if c >= 10 { break } } }
消息
子主题
JSON
序列化与反序列化
序列化
将Go语言数据转为JSON
json.Marshal(goData)
结构体
map[string]interface{}
[]map[string]interface{}
[]struct
反序列化
将JSON数据转为Go语言数据
json.Unmarshal([]byte{},*pointer)
结构体指针
[string]interface{}
切片指针
读写JSON文件
编码
将Go语言的数据写出到JSON文件
encoder := json.NewEncoder(dstFile)
encoder.Encode(goData)
解码
将JSON文件中的数据读取为Go语言数据
decoder := json.NewDecoder(srcFile)
decoder.Decode(goDataPtr) `参数为指针`
反射
关于反射
反射具有强大的功能
反射是用程序检查其所拥有的结构,尤其是类型的一种能力
这是元编程的一种形式
可以在[运行时]通过反射来分析一个结构体
检查其类型和变量(类型和取值)和方法
动态地修改变量和调用的方法
这对于没有源码的包尤其有用
这是一个强大的工具, 除非真的有必要, 否则应当避免使用或者小心使用
使用场景
做对象得序列化Marshal 和 Unmarshal
获取对象信息
修改对象属性值
调用对象方法
API
oType := reflect.TypeOf(obj)
t.Name()
kind := oType.Kind()
Kind是对具有[ 系统类型 ] 的枚举
全部[ 系统类型 ]

t.NumField()
t.NumMethod()
structField := oType.Field(i)
structField.Name
structField.Type
method := oType.Method(i)
method.Name
methodType := method.Type
argNum := method.Type.Numln()
参数个数
artType := method.Type.ln(0)
第0个参数的类型
t.FieldByIndex([]int{0 , 1})
找出第0个父结构体中的第1个属性
oValue := reflect.ValueOf(boj)
fieldValue := oValue.Field( i )
fieldValue := value.FiledByIndex( []int{0, 0} ).interface()
找出第0个副结构体中的第0个属性的值
oPtrValue.Elem()
获取地址value中的值的value
oPtrValue.Elem().CanSet()
检查当前地址的value内的值是否可以改变
可改变条件: 可寻址 + 不来自非导出字段
oPtrValue.Elem().SetInt(999)
value.SetString("Jack")
nameValue := value.FieldByName("name")
isValid := value.IsValid()
nil(0值)非法
非常罕见
kind := value.Kind()
methodValue := oValue.Method( i )
methodValue.Call([]reflect.Value{val1, val2})
调用一个方法
此处主语是一个" 方法型 "的value
Scoket
Basic programming
TCP
Servers
listener_scoket, err := net.Listen("tcp","127.0.0.1:8898") // 建立监听
listener_scoket.Close() // 关闭连接
conn, err := listener_scoket.Accept() // 接受请求
remoteAddr := conn.RemoteAddr() // 远程地址
numOfBytes,err := conn.Read(buf // 接受缓存字节切片) // 读取数据,返回自己长度
conn.Write([]byte("hello"))
conn.Close()
Client
conn,err := net.Dail("tcp", "127.0.0.1:8898")
conn.Close()
conn.Write([]byte("hello"))
numOfBytes,err := conn.Read(buf)
reader := bufio.NewReader(os.Stdin)
data, isPrefix, err := reader.ReadLine()
传输层 长连接
UDP
Servers
udp_addr, err := net.ResolveUDPAddr("udp", ":8000")
parseAddr
func ResolveUDPAddr func ResolveUDPAddr(network, address string) (*UDPAddr, error) ResolveUDPAddr returns an address of UDP end point. The network must be a UDP network name. If the host in the address parameter is not a literal IP address or the port is not a literal port number, ResolveUDPAddr resolves the address to an address of UDP end point. Otherwise, it parses the address as a pair of literal IP address and port number. The address parameter can use a host name, but this is not recommended, because it will return at most one of the host name's IP addresses.
parameter
network : udp / udp4 / udp6 string
address :
ipv4: 127.0.0.1:8888
hostname : www.baidu.com:https (hostname + The name of the protocol )
conn, err := net.ListenUDP("udp", udp_addr)
conn.Close()
n, reddr,err := conn.ReadFromUDP(buf[0:])
fmt.Println("消息是", string(buf[0:n]))
_ , err := conn.WriteToUDP([]byte("Hello"), raddr)
Client
conn, err := net.Dail("udp","127.0.0.1:8848")
conn.Write([]byte("hello"))
conn.Read(buffer)
传输层 短链接 ( 广播 )
HTTP
Servers
http.HandleFunc("/hello", func(writer, request) { w.Write([]byte("hello")) })
参数一:路由
参数二:匿名处理函数
writer
给客户端的写出器
request
封装了客户端的所有信息
http.ListenAndServe("127.0.0.1:8080", nil)
参数一: 监听地址
参数二: 处理函数
Client
resp, err := http.Get(url)
resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
string(bytes)
resp, err := http.Post(url, "application/x-www-form-urlencoded",strings.NewReader("id=nimei&age=30"))
应用层协议
WebScoket
Package
net
Conn
Read(b []byte) (n int, err error)
recommend
Read reads data from the connection. 从链接读取数据
Read can be made to time out and return an error after a fixed 读取数据如果超时会返回error
time limit; see SetDeadline and SetReadDeadline. 时间限制可以看SetDeadLine方法
parameter
接收一个 [ ]byte
return
len( []byte )
error
同write
Write(b []byte) (n int, err error)
recommend
Write writes data to the connection. 链接内写入数据
Write can be made to time out and return an error after a fixed
time limit; see SetDeadline and SetWriteDeadline.
parameter
return
同read
表示层
表示层可以简单理解为, 将应用程序中应用的数据转化为网路中应用的数据, 例如将json转化为字节或者Read和Write方法入参需要入参字节切片, 就是将数据转化为字节
Close() error
Close closes the connection. 关闭链接
Any blocked Read or Write operations will be unblocked and return errors. 所有阻塞线程的Read和Write操作将会关闭并且返回error
会话层
会话层是在应用层控制链接的建立和断开的, 如果数据传输完毕, 需要断开连接,否则会浪费资源
LocalAddr() Addr
返回本地网络地址
RemoteAddr() Addr
返回远程网络地址
SetDeadline(t time.Time) error
连接超时机制
Deadline不是超时(timeout)。一旦设置它们永久生效(或者直到下一次调用SetDeadline), 不管此时连接是否被使用和怎么用。所以如果想使用SetDeadline建立超时机制,你不得不每次在Read/Write操作之前调用它。 你可能不想自己调用SetDeadline, 而是让net/http代替你调用,所以你可以调用更高级的timeout方法。但是请记住,所有的超时的实现都是基于Deadline, 所以它们不会每次接收或者发送重新设置这个值(so they do NOT reset every time data is sent or received)。
parameter
time.time
绝对值时间
return
error
服务器超时设置
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error