# 要点概述
## 1 go语言特性
自动垃圾回收,丰富的内置类型,函数多返回值,匿名函数与闭包,类型与非侵入式接口,并发编程
(goroutine),反射机制。
## 2 基本语法
### 2.1 变量
#### 2.1.1 变量声名方式
1. var v1 int or var v1 int = 3(声明并赋值)
2. var v2 = 3 (自动确定类型)
3. v3 := 3
#### 2.1.2 其它
多重赋值:
i, j = j, i (交换值)
支持匿名变量(_)
### 2.2 常量
常量定义:
const Pi float64 = 3.14159
const(
size int = 3
eof = -1
)
const与itoa用法,一般可用于枚举型
### 2.3 数据类型
#### 2.3.1 基本类型
1. 布尔类型:bool(只能接受true和false,不能接受其它类型赋值)
2. 整型:int8, byte(uint8),int16,uint等
3. 浮点类型:float32, float64
4. 复数类型:complex64, complex128(c := 3 + 3i)
5. 字符串:string (s1+s2, len(s), s[i], 两种遍历方式)
6. 字符:byte(utf-8字符),rune(unicode字符)
7. 错误类型:error
#### 2.3.2 复合类型
1. 指针(类似c语言)
2. 数组(元素数量固定)array := [3]int{1,2,3}
3. 切片(动态数组)
slice := []int{1,2,3}
slice := make([]int, 3) or make([]int, 3, 5) or 基于数组/切片创建
cap(),len(),copy()和append()函数
4. 字典——map
map := map[keyType] valueType {key: value}
map := make(map[keyType] valueType, 3)
delete()和find()函数
5. 通道——chan
gorontinue通信
### 2.4 流程控制
#### 2.4.1 条件语句(if)
1. 条件语句不需要()
2. if之后,条件语句之前可以添加初始化语句,用;间隔
3. return语句不能包含在if...else...中
if v := 3 ; v >=3 { //左花括号必须与if在同一行
...
return r // No
}
#### 2.4.2 选择语句(switch)
1. 条件表达式不限定为常量或者整数
2. 不需要break来退出一个case
3. 只有在case中添加了fallthrouth关键字,才会继续执行下一个case
4. swicth后面可以没有条件表达式,相当于if...else...逻辑
#### 2.4.3 循环语句(for)
1. 不需要()
2. 不支持使用,来实现多变量初始化,可以使用平行赋值语句
3. 支持break, continue
#### 2.4.4 跳转语句(goto)
谨用
### 2.5 函数
#### 2.5.1 函数定义
func name(arg1, arg2) (ret1, ret2){
...
}
#### 2.5.2 特性
1. 支持多返回值
2. 不定参数
func name(args ...int ) 参数个数不定,类型为int
fun name(args ...interface{} ) 参数个数不定,类型不定
调用方式:name(args...)
3. 匿名函数
f := func(arg1, arg2) (ret1, ret2)
4. 闭包
### 2.6 错误处理
#### 2.6.1 error接口
接口定义如下:
type error interface{
Error() string
}
凡是实现了Error() string方法的类都属于error类型
#### 2.6.1 defer
函数返回之前执行,一般用于释放资源等操作,当一个函数有多个defer时,调用顺序为先进后出,同时
支持匿名defer,定义如下:
defer func(){
...
}
#### 2.6.1 panic()和recover()
*panic函数*
用于报告错误,即在一个函数中执行panic()时,正常的执行流程将终止,但是defer相应函数
依然执行,另外panic()会向上层执行panic()
*recover函数*
用于处理错误,一般用于defer中
## 3 面向对象
### 3.1 值与引用
go语言可以算都是基于值的传递,尤其对于数组,在c语言里是传地址,而在go中是传值(注意切片,map,
channel,interface的方式,实际也可以看成是传值)
### 3.2 结构体
#### 3.2.1 定义与初始化
go中的结构体相当于其它语言中的class
定义 :
type Rect struct{
x, y int
width, height float
}
初始化:
r1 = new(Rect)
r2 = Rect{1,1,1,1}
r3 = Rect{width: 1, height:1}
#### 3.2.2 继承
go采用组合的方法实现继承
例子如下:
type Base struct {
Name string
}
func (base *Base) Foo() {...}
func (base *Base) Bar() {...}
type Foo struct {
Base or *Base (注意区别)
...
}
func (foo *Foo) Bar() {
foo.Base.Bar()
...
}
上面首先定义基类Base,并且定义Foo()和Bar()两个方法;然后定义Foo(组合继承Base),而且
重写了Bar方法.foo.Foo()就会调用Base的Foo()方法
注意:命名冲突时,外层覆盖里层的
#### 3.2.3 包可见性
首字母大写则表示对其它包可见
#### 3.2.4 方法值与方法表达式
不管是基于指针对象还是基于非指针对象的方法,指针对象与非指针对象(得有变量名,可以取到地址)均可以对方法进行调用,因为编译器会
作相应的隐式转换.
另外可以根据基于非指针对象的方法生成相应的基于指针对象的方法,所以基于指针的对象的方法一般比其相应的非指针对象的方法要多.
*例子1*
p := Point{1, 2}
q := Point{4, 6}
distanceFromP := p.Distance // method value
fmt.Println(distanceFromP(q)) // "5"
*例子2*
p := Point{1, 2}
q := Point{4, 6}
distance := Point.Distance // method expression
fmt.Println(distance(p, q)) // "5" 第一个参数为接收器参数,其它的均为函数参数
### 3.3 接口
#### 3.3.1 非侵入式接口
一个类只要实现了某个接口的所有函数,就等于实现了这个接口;终于再也不用画类的继承树图了
#### 3.3.2 接口赋值
1. 对象赋值给接口(两种方式:直接赋值与取地址赋值)
2. 接口赋值给接口
两个接口如果方法相同,那么这两个接口实际上没有区别;另外当把接口A赋值给接口B时,B接口
的方法应该是A接口方法的子集
#### 3.3.2 接口查询
_, ok = interface1.(interface2) interface1所指向的对象是否实现interface2接口的方法
特例 interface1.(Type) interface1所指向的对象是否属于Type类型
另外 interface1.(type) 获取interface1所指向的对象的类型(用在switch语句中)
#### 3.3.3 任意类型
var v1 interface{} = 1
var v1 interface{} = "string"
#### 3.3.4 接口值
接口由两部分组成,一个具体类型与具体类型对应的值,它们被称为动态类型与动态值.
*例子*
var w io.Writer
w = os.Stdout
声明一个未初始化的接口,其动态类型与动态值均为nil.将os.Stdout赋值给w后,w的动态值与动态类型则被赋值
## 4 并发编程
### 4.1 并发模型
1. 多进程
2. 多纯种
3. 基于回调的异步非阻塞IO
4. 协程
### 4.2 goroutine
goroutine是Go语言的轻量级线程,由Go的runtime管理.
使用方法: go functionName()
### 4.3 并发通信
共享数据和消息机制是两种常见的并发通信模型;Go语言采用消息机制的通信模型,称为channel.
"不要通过共享内存来通信,而应该通过通信来共享内存"
### 4.4 channel
#### 4.4.1 基本语法
channel是类型相关的,定义方式如下:
var channelName chan Type
var ch chan float32
var m map[string] chan bool
make(chan int)
单向channel:
var ch1 chan<- float32 写
var ch2 <- chan float32 读
var ch3 := chan<- float(ch)
var ch4 := <-chan float(ch)
存取channel(channel为空时,读取数据操作会阻塞;channel满时,存储数据操作会阻塞):
ch <- value
value := <-ch
关闭channel:
close(ch)
#### 4.4.2 缓存
不带缓存的channel也叫做同步channel, 用于不同goroutines之间进行同步.
带缓存的channel通常用于异步操作,只要缓存区不满也不空,读取操作均不会阻塞.
channel支持缓冲机制, ch = make(ch int, 1024)
for i := range ch {
...
}
#### 4.4.3 超时机制
采用select实现超时机制:
timeout := make(chan bool, 1)
go func(){
time.Sleep(le9)
timeout <- true
}()
select {
case <- ch :
case <- timeout:
}
### 4.5 多核并行化
如若编译器版本不支持多核并行,则可以通过设置环境变量GOMAXPROCS的值:runtie.GOMAXPROCS(num)
### 4.6 同步
go语言也支持同步锁,包括sync.Mutex和sync.RWMutext
### 4.7 全局惟一性操作
sync.Once可以保证某个方法全局只执行一次,例如:
var once sync.Once
once.Do(func) 表示func函数全局只会执行一次
### 4.8 并发的退出
*利用channel传递信息*
在goroutine中加入退出channel,当该channel中有数据可读时表示退出该goroutine.
abort := make(chan struct{})
go func() {
os.Stdin.Read(make([]byte, 1)) // read a single byte
abort <- struct{}{}
}()
func main() {
// ...create abort channel...
fmt.Println("Commencing countdown. Press return to abort.")
select {
case <-time.After(10 * time.Second):
// Do nothing.
case <-abort:
fmt.Println("Launch aborted!")
return
}
launch()
}
*通过close channel*
当channel关闭后,不能再向channel写数据,但是可以从channel中读取数据,当读完channel中原来的数据后,继续读则
返回零值.
var done = make(chan struct{})
func cancelled() bool {
select {
case <-done: return true
default:
return false
}
}
go func() {
os.Stdin.Read(make([]byte, 1)) // read a single byte
close(done)
}()
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
defer n.Done()
if cancelled() {
return
}
for _, entry := range dirents(dir) {
// ...
}
}
## 5 网络编程
### 5.1 socket编程
#### 5.1.1 建立连接
TCP: conn, err = net.Dial("tcp","xxx.xxx.xxx.xxx:xxxx")
UDP: conn, err = net.Dial("udp","xxx.xxx.xxx.xxx:xxxx")
ICMP: conn, err = net.Dial("ip4:icmp","xxx.xxx.xxx.xxx:xxxx")
#### 5.1.2 读写
conn.Write()
conn.Read()
#### 5.1.3 Dial()
Dial()函数其实是对DialTCP(),DialUDP(),DialIP()和DialUnix()的封装;所以在代码中可以直接
使用这些函数
### 5.2 http编程
#### 5.2.1 Client
*Client常用函数(http.Get(),http.Post()等)*
func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (r *Response, err error)
*高级封装*
前面介绍的http.Get()实际上是http.DefaultClient.Get(),从而也说明Client是可以自定义的,
Client定义如下:
type Client type{
//Transport用于确定http请求机制(是否长链接,ssl,连接数等)
Transport RoundTripper
//重定向之前调用该函数
CheckRedirect fuc(req *Request, via []*Request) error
//cookie信息
Jar CookieJar
}
用户可以自定义Transport,CheckRedirect,Jar;从而根据需要实现自定义Client.
### 5.3 RPC
#### 5.3.1 相关概念
标准库提供了net/rp包实现了RPC协议的相差细节. 在RPC服务端,可将一个对象注册为可访问的服务,之后
该对象的公开方法就能够以远程的方式提供访问。而且一个RPC服务端可以注册多个不同类型的对象,但是
不允许注册同一类型的多个对象.
对象中的方法需要满足以下要求才可以被远程访问:
1. 对象方法必需可供外部调用(首字母大写)
2. 必须有两个参数,第二个必须是指针
3. 方法必须返回error类型的值
如 func (t *T) MethodName(arg T1, reply *T2) error
#### 5.3.2 例子
*服务端*
obj := new(Obj)
rpc.Register(obj)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":3333")
if e! = nil {
log.Fatal("listen error: ", e)
}
go http.Serve(l, nil)
*客户端*
client, err = rpc.DialHTTP("tcp", address+":3333")
同步:
err = client.Call()
异步:
asyCall := client.Go()
replyCall := <-asyCall.Done
### 5.4 JSON解析
#### 5.4.1 编码
json.Marshal()
func Marshal(v interface{}) ([]byte, error)
#### 5.4.2 解码
json.Unmarshal()
func Unmarshal(data []bite, v interface{}) error
注意:已知数据结构的解码与未知数据结构的解码
#### 5.4.3 JSON流式读写
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
## 6 安全编程
TODO
## 7 进阶话题
TODO
# 个人想法
优雅的go
*时间: 2016/01/28*
有疑问加站长微信联系(非本文作者)