Go学习指南

时间:2021-05-08 01:54:31

学习Golang书籍&资料:

1. The Go Programming Language Specification:  http://golang.org/ref/spec

2. How to Write Go Code: http://golang.org/doc/code.html

3. Effective Go: http://golang.org/doc/effective_go.html

4. Go语言编程.pdf

5. Go语言程序设计.pdf

6. 学习GO语言.pdf

7. Go语言·云动力.pdf

8. Go Web编程

https://github.com/tiancaiamao/go-internals/tree/master/ebook
------------------------------------

学习编程语言前,需明白:

一. 什么是编程what is programming?

编程就是控制某种系统或工具实现具体功能或目标的过程. 至于哪种系统, 哪种工具, 什么功能, 什么目标? 由具体业务功能决定. 简言之, 编程就是一种控制xx实现xx的过程.

二. 怎样编程how to programming?

编程=算法+数据结构+语言

算法是控制实现过程的步骤序列.

数据结构是控制实现过程的数据方式,包括逻辑表示方式与物理存储方式.

语言是控制实现过程的指令系统.

其中, 算法与数据结构都由语言来表达.

学习编程语言时,应按纲:

一. 设计目标

轻松编程, 快速编译, 高效执行.

二. 运行机制

Golang是系统编程语言, 即Golang编程是控制系统实现功能.

源代码compile by compier to generate目标代码.

目标代码link with native library to generate可执行程序

如果是虚拟机编程语言, 即控制虚拟机(间接控制系统)实现功能.

源代码compile by compier to genrate字节码.

虚拟机load class byte code to running.

静态编译型语言:

源代码-->|被编译器编译|-->目标代码-->|与本地库链接|-->本地程序-->|控制系统实现功能|

动态解释型语言:

源代码-->|被编译器编译|-->字节码-->|被虚拟机解释|---->|控制系统实现功能|

相比可知: 动态解释型语言,由于虚拟机封装底层系统应用,提供上层接口. 大大提高程序在不同系统之间的可移植性及一致性.    

三. 语言特性

在Golang中, 主要的言特性:

类型后置

延迟函数: defer function & panic() & reconver

协程通信: go router & channel

尾调用: It is already there in 6g/8g for certain cases, and in gccgo somewhat more generally.We do not currently plan to change the language to require that compilers implement tail call optimization in all cases. If you must have a tail call, you use a loop or a goto statement.

注意: Golang不支持尾调用. 要用goto来实现其效果.

--------------------------------------------

深入后理解:

(1) 指针与反射: pointer & reflect

(2) 协程与通道: goroutine & channel

(3) 延迟与错误恢复: defer & panic(), reconver()

(4) 松接口与方法: loose interface

(5) 函数类型&多值返回:    func type & multi-return value

四. 语法类库, Golang的语法更像C+Python+Lua

1. 字符集

Golang使用Unicode字符集,UTF8编码.内置包encoding/utf8提供字符集相关的操作.

2. 词汇

2.1 保留字(Keyword): Golang保留字有20个:

break       default         func         interface     select

case         defer            go            map             struct

chan        else              goto         package      switch

const       fallthrough  if              range           type

continue for                import      return          var

可以分成3大类:

(1) 声明定义: package, type, var const, func, chan

(2) 控制语句:

(2.1) 条件选择: if-else, switch-case-default-fallthrough, select-case-default

(2.2) 循环迭代: for的三种形式:

for initial;condition;increment{...}

for initial;condition{...}

for {...}

(2.3) 跳转中断: break, continue, goto, return

(3) 特性操作: go, defer, import, range

2.2 标识符(Identity):

Golang的标识符由字母,数字,下划线组成.

(1)并且不能数字打头.

(2)不能与保留字相同.

Golang约定:

(1)公有标识符: 大写字母打头的标识符. 公有标识符可以导出包外, 即可以包外访问.

(2)私有标识符: 小写字母或下划线打头的标识符, 私有标识符不能导出包外, 即只能包内访问. 中文都是小写字母, 都属于私有标识符.

例如: const W全局变量 int = 123, 如果没有W不能在包外访问.

(3)下划线(_)标识符: 自动舍弃赋值, 不供访问. 否则"cannot use _ as value"

2.3 直接量(Literal):

直接量是值的数据表示形式.Golang的下述类型的直接量:

(1)指针类型: nil, 只有指针类型才可能nil.

(2)bool类型: true, false

(3)int,intN,uint,uintN类型:

8进制0前缀

10进制无前缀

16进制0x前缀

注意: Golang的直接量不支持L,F后缀! 例如: var a int = 123L会报错.

(4)floatN类型:

小数形式

指数形式

注意: Golang的直接量不支持L,F后缀! 例如: var a float32 = 123F会报错.

(5)rune类型: 'C',单引括起来的单个字符或转义序列.Golang支持的转义序列:

\a U+0007 alert or bell

\b U+0008 backspace

\f U+000C form feed

\n U+000A line feed or newline

\r U+000D carriage return

\t U+0009 horizontal tab

\v U+000b vertical tab

\\ U+005c backslash

\' U+0027 single quote (valid escape only within rune literals)

\" U+0022 double quote (valid escape only within string literals)

(6)string类型: "...", `...`,双引,反引括住的字符序列. 

注意: Golang的字符串不能使用单引.

(7)array类型: {...}

(8)slice类型: []{...}

(9)map类型: {k=v...}

(10)struct类型: struct{...}

(11)interface类型: interface{...}

(12)func类型: func(){...},函数直接量

2.4 运算符(Operator):

(1)算术运算符

(2)比较运算符

(3)逻辑运算符

(4)位运算符

(5)赋值运算符

(6)逗号运算符

(7)条件运算符

(8)字串运算符

(9)地址运算符

(10)channel运算符

2.5 注释符(Comment):

(1)单行注释//

(2)多行注释/*...*/

(3)文档注释,即组件定义前的注释,可用godoc提取.

2.6 界定符: 词汇的分隔符.

换行符(\n)或分号(;)表示语句结束.

续行符(\)取消换行: Golang没有续行符

空格符(Space)

制表符(\t)

括弧: 圆括弧(...), 方括弧[...], 花括弧{...}

3. 数据类型, 直接量, 变量, 常量

数据类型运算:

x.(type)只能在switch结构使用,否则抛错:use of .(type) outside type switch

defer func() {

if x := recover(); x != nil {

fmt.Printf("x=%v,m=%v\n", x.(type), x)

}

}()

解决办法: 使用felect.TypeOf(x)

数据结构指数据在计算机的逻辑表示与物理存储方式.

数据类型指某类数据的值集及其操作集.抽象数据类型DAT表示为(D,S,P),其中D表示该数据的值集,S表示该类数据的关系集,P表示该类数据的操作集.

直接量指值的数据表示.

变量指值会变的量.

常量指值会变的量.

C中定义变量,常量需要指定存储类别, 数据类型. Golang没有存储类别, 类型声明后置.

Golang约定:

(1)变量使用var显式声明,或用:=隐匿定义.

显式声明: var vname type=value

隐式定义: vname:=value

换言之 :=取代了var与type, 自动根据value推断类型.

(2)常量使用const显式声明.

显式声明: const cname type=value

(3)Go支持分组声明. 即同时声明多个变量,常量或引入多个包. 在分组声明中:

变量的缺省类型向后推断, 即变量没有指定类型时, 默认其后第一个显式类型.

常量的缺省定义向前推断, 即常量没有指定定义时, 默认其前第一个显式定义.

例如:

var(

a,

b,

c int; //则a,b,的类型默认为c的类型.

)

const(

ac int = iota

bc //默认也是iota

cc //默认也是iota

)

(4) Go支持iota枚举. 在const块自动清0, 调用后自动加1.

Golang数据类型:

指针类型: nil

----------------------------------------

bool: true, false

integer:

int, int8, int16, int32, int64, rune

uint, uint8, uint16, uint32, uint64, byte

其中byte是uint8的别名, rune是int16的别名, 而byte, rune常用来表示字符.

float:

float32, float64

complex:

complex64, complex128

其中复数一般使用直接量RE+IMi或complex()函数初始化.例如:

var re float32 = 123

var im float32 = 34

var a complex64 = complex(re, im)

fmt.Println(a)

使用complex()函数,要求re,im的类型必须是float32或float64,而且必须相同.如果是float32,则生成complex64. 如果是float64,则生成complex128.

----------------------------------------

rune: 单引号括住的单个字符或转义序列

string: 双引,反引括住的字符序列. 其中反引取消换行与转义符. 注意: 在Golang中string类型不支持单引! 例如:

var str1 string = `这\t是

一个

测试`

var str2 string = "这\t是"

var str3 string = '这\t是' //empty character literal or unescaped ' in character literal

fmt.Println(str1)

fmt.Println(str2)

fmt.Println(str3)

可以使用encoding/utf8包来处理utf8编码, 例如:

import fmt "fmt"

import "unicode/utf8"

func main() {

str := "dsjkdshd这是一个小小的测试sdh....js"

fmt.Printf("String %s\nLength: %d, Runes: %d\n", str, len(str), utf8.RuneCount([]byte(str)))

}

----------------------------------------

array: [xxx]type{...}, 注意: 在Golang中,

(1)array的长度是类型的组成部分, 不同长度表示不同类型. 例如:

var a [4]int = [...]int{1, 2, 3, 3}

var b [3]int = [...]int{3, 4, 5}

a = b //cannot use b (type [3]int) as type [4]int in assignment

(2)array直接量使用"..."表示array长度是元素个数. 注意: "..."只能用于array literal, 例如:

var b [...]int = [...]int{3, 4, 5}//var b [...]int = [...]int{3, 4, 5}

slice: []type{...}, slice底层封装array, start, stop, copacity. slice会自动扩容.

array与slice的区别:

(1)array必须指定长度,使用过程容量固定.

(2)slice不能指定长度,使用过程自动扩容, 但其底层(array,start,stop,capacity)会发生变化.

例如:

var arr [4]int //定义array

var sar []int = arr[:] //定义slice

sar2 := append(sar, 123) //扩容slice, 生成另一个slice, 其底层(array, start, stop, capacity)也改变.

sar[0] = 112 //测试赋值

fmt.Println(arr[0]) //值为112

fmt.Println(sar[0]) //值为112

fmt.Println(sar2[0]) //值为0

在Golang中, slice是套在array上的一个封装器.

map: map[ktype]vtype{...}

map索引返回<value,status>, value表示键的值, status表示键的状态(即键是否存在)!

chan: 在Golang中chan更像是C中的存储类别. 例如 chan int ch=make(chan int).

在Golang中, slice, map, chan必须使用new(), make()函数来创建! 没有规律, 实在蛋疼!

----------------------------------------

struct: 定义结构体

interface: 定义接口集

struct与interface的结合:

在Golang中, 使用struct+interface实现OO思想.

----------------------------------------

package:在Golang中, package与folder的名字可以不同! 具体操作时, import的对象folder的相对路径, 而代码中引用却是package. 当然, 可以使用import <package> "path"方式来覆盖默认的package. 这又何苦定义package? 另外, package支持层次吗? 例如声明package pk1.pk2

func: 支持func为右值,却无func类型, 即不能显式声明a为func类型,哪么a又是什么类型?

a := func() {

str := "dsjkdshd这是一个小小的测试sdh....js"

fmt.Printf("String %s\nLength: %d, Runes: %d\n", str, len(str), utf8.RuneCount([]byte(str)))

}

a()

在Golang中,func支持闭包吗?

----------------------------------------

简结, 在Go1中, 类型系统杂乱无章, 无规可遁! 只能就近分组,方便记忆!

关于Golang数据类型的更深入理解:

----------------------------------------

Golang的数据类型分3类(基本类型,复合类型,功能类型),  2种方式(值方式,指针方式)

值方式:

基本类型(Primitive type), 最基本的值类型

(1)布尔型: bool

(2)整型: int, intN, uint, uintN

(3)浮点型: floatN

(4)复数型: complexN

(5)字符型: byte, rune

(6)字串型: string

(6.1) 错误类型: error, Go内置error type以及errors package.

基本类型不能使用初始表{...}初始化!


复合类型(Composite type), 在基本类型上定义的复合值类型.

(1)数组类型: array

(2)切片类型: slice

(3)映射类型: map

(4)结构类型: struct

复合类型可以使用初始表{...}初始化! 初始表有二种形式, 按顺序, 或按索引.


功能类型(Function type),  在基本值类型,复合值类型上定义的功能类型.

(1)通道类型: chan

(2)函数类型: func

(3)接口类型: interface

行为类型只提供某种行为能力,不涉及数据. 通道类型在goroutine之间提供channel的行为能力.但不涉及数据.


指针方式: 每种值类型都有指针. Golang有指针类型,但没有指针运算. 指针不是类型, 只是解决效率方式.

nil是指针类型的直接量. 只有指针类型才能赋值nil.

interface类型的专有操作:

(1) Comma-ok断言:
<interface>.(T)

其中<interface>必须是interface类型.否则cannot type switch on non-interface value e (type Student)

(2) switch测试:

switch <interface>.(type) {
case XXX:
}

其中<interface>必须是interface类型. <interface>.(type)语法不能在switch外的任何逻辑里面使用.

interface陷阱: interface类型只能做为参数类型, 不能做为返回类型!

详情: http://golang.org/doc/faq#nil_error

Under the covers, interfaces are implemented as two elements, a type and a value. The value, called the interface's dynamic value, is an arbitrary concrete value and the type is that of the value. For the int value 3, an interface value contains, schematically, (int, 3).

An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type. If we store a pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (*int, nil). Such an interface value will therefore be non-nileven when the pointer inside isnil.

简言之: 在Go中, interace的实现包含二个元素<type,value>, interface为nil当且仅当<nil,nil>. 当声明

var err *MyErrorImpl 或 var err *MyErrorImpl =nil时

已经变成了MyError<*MyErrorImpl,nil>, 故其做为MyError返回已经不再是nil. 解决办法有二:

(1)照官方说的, 先不声明, return时先定义.

(2)直接返回MyErrorImpl,即struct而不是interface

示例:

type Inf interface {

Info() string

}

type InfImpl struct {

info string

}

func (self *InfImpl) Info() string {

return self.info

}

func returnInf() *InfImpl {

var i *InfImpl = nil

return i

}

func main() {

fmt.Println("should nil")

ri := returnInf()

if ri != nil {

fmt.Println("not nil")

} else {

fmt.Println("is nil")

}

}

4. 运算符, 内置函数, 表达式

运算符:

(1) Arithmetic operators: +, -, *, /, %

(2) Comparison operators: ==, !=, <, <=, >, >=

(3) Logical operators: !, &&, ||

(4) Address operators: &, *

(5) Receive operator: <-, 例如v2 = <-ch

(4) 位运算符: ~, &, |, ^

--------------------------

内置函数:

关于panic(),recover()的陷阱:

panic()会触发程序中断, 除非使用recover()恢复. 但recover()只是简单返回panic()的参数. 仅仅根据result == nil判断是否发生中断, 会有陷阱. 因为panic(nil)触发中断,但其返回值却是nil.

recover()最好也是result,ok:=recover(),后面ok表示是否触发.

--------------------------

表达式:

(1)Method expressions

MethodExpr = ReceiverType "." MethodName .

ReceiverType = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .

更多参考<The Go Programming Language Specification>

5. 语句

5.1 表达式语句

5.2 空语句

5.3 语句块/复合语句

5.4 控制语句

5.4.1 条件选择:

if-else(三种形式)

switch-case-default-fallthrough(二种形式)

select-case-default

5.4.2 循环迭代

for(三种形式)

注意: Golang不支持do, while关键字. 迭代借助range实现.

5.5 跳转中断:

break

continue

goto

return

fallthrough

5.6 异常处理:

defer,panic(),recover()

在Golang的条件选择, 循环迭代中条件表达式都可省略(), 同时可以包含初始化部分. 在初始化部分隐匿声明的变量的作用域是其后的语句块.

6. 函数

(1)语法定义

func (receiver ReceiveType) fname(param ParamType) ReturnType{

...

(2)形参声明

普通形参: Go 支持普通参数

默认形参: Go不支持默认参数

变长形参: ...,  Go支持变长参数, 等价slice, 可用range迭代. 其也表示unpack的意思.

多值返回: 如果声明返回值变量,则不需显式return.

(3)实参传递:

按位置传递: Go支持按位置传递.

按命名传递: Go不支持命名传递

变参传递: Go支持变参传递.

变参

接受不定数量的参数的函数叫做变参函数。定义函数使其接受变参:

func myfunc(arg ... i n t ) { }

arg ...int 告诉Go 这个函数接受不定数量的参数。注意,这些参数的类型全部是int。在函数体中,变量arg 是一个int 类型的slice:

f o r _, n := range arg {

fmt.Printf("And the number is: %d\n", n)

}

如果不想指定变参的类型,可用空接口interface{}(参阅第6 章)。假设有另一个变参函数叫做myfunc2,下面的例子演示了如何向其传递变参:

func myfunc(arg ... i n t ) {

myfunc2(arg...) 按原样传递

myfunc2(arg[:2]...) 传递部分

}

(4)内建函数:

close     new         panic         complex

delete   make       recover      real

len        append    print          image

cap       copy         println

其中:

close: 用于channel通讯,关闭channel.

delete: 用于删除map中的元素.

len和cap: 返回string, slice, array, map的长度与容量.

new: 用于各种类型的内在分配,返回指针.

make: 用于map, slice, channel的初始化, 返回实例.

copy: 用于复制slice.

append: 用于追加slice.

panic和recover: 用于异常处理机制.

print和println: 用于底层打印函数, 可在不引入fmt包的情况下使用. 主要用于调试.

complex, real和imag: 用于处理复数.

new()与make()的区别:

• new(T) 返回*T 指向一个零值T

• make(T) 返回初始化后的T

其中make 只能用于slice,map 和channel。

对于方法而言, 其声明为Type或者*Type好像都没有局限. 如果Type.proerty查不到,会自动(&T).property.没有像C哪样, 如果是指针则使用->.

main()函数与 init()函数: 

(1)Go保留main()函数与init()函数. 每个main package至少一个main()函数, 每个package可有任意init()函数. 其执行顺序参见下图:

(2)函数数不能接收参数也不能返回值: func main must have no arguments and no return values

Go学习指南

函数变量,函数常量,函数直接量,函数指针.:

任何直接量都不能使用&获取地址.

/*

不能获取函数的地址

var f *func(int) bool = &isOdd

cannot take the address of isOdd

但可以赋给变量,然后取变量的地址.

var f func(int) bool = isOdd

odd := filter(&nums, &f)

这就像你试图获取直接量的地址:

func main() {

var pa *int = &345

fmt.Printf("even=%v\n", *pa)

}

cannot take the address of 345

*/

7. 高级特性

> CGO

> 指针: Go有指针,但是没有指针运算. 你不能用指针变量遍历字符串的各个字节.    

8. 命名空间:包

定义包: 

package <package_name>

加载包:

import <package_alias> "package_path",

必须注意: import "package_path", references <package_name >. 

即import的值是path, 而references的值是name, 

不管path还是package都是相对$GOPATH.

其中可在import重命名package_name! 哪package <package_name>是否多余了?

Golang的package与C++的namespace都是用于组织命名空间,避免命名冲突. 相同package下的变量可以互相访问. 

Go学习指南

包的操作:

Go学习指南

9. 库:内置包

参考<Directory /src/pkg>

10.程序结构

$GOPATH:

src: 源代码

pkg: 依赖包

bin: 二进制

go命令的参数<package>映射为$GOPATH/src/<package>,一般要求package xxx与目录名相同! 但都习惯在目录下面创建一个package main.用来做为应用的入口.

五. 环境配置

1. 搭建开发环境

1). 安装go 1.2 for windows

2). 安装eclipse

3). 安装goclipse

4). 安装gocode

5). 安装gdb

Linux下安装Golang:

wget http://go.googlecode.com/files/go1.2.1.linux-amd64.tar.gz

tar -xzvf go1.2.1.linux-amd64.tar.gz -C /usr/local/

cd /usr/local/go/src

source all.bash

如果看到则表示成功:

ALL TESTS PASSED

---

Installed Go for linux/amd64 in /usr/local/go

Installed commands in /usr/local/go/bin

*** You need to add /usr/local/go/bin to your PATH.

2. 配置环境变量

#java

export JAVA_HOME=/usr/local/java

export PATH=$JAVA_HOME/bin:$PATH

#go

export GOROOT=/usr/local/go

export GOOS=linux

export GOARCH=amd64

#export GOBIN=$GOROOT/bin 影响go install

export GOTOOLS=$GOROOT/pkg/tool

export PATH=$PATH:$GOBIN:$GOTOOLS

3. 命令行及选项

go version

go env 列述GO环境变量

go get <packageUrl>

go install <package>: 不要随便设置GOBIN, 使用go install命令会把编译后的结果放到$GOBIN

go build <package>

go run <path>: 必须指定go文件的绝对路径, 只是用作一次性测试.

go list <package>: 列出当前目录或package下的可builtable的package

go test <package>: 执行对应package下面的测试程序,即以_test.go结尾的模块.

--------------------------------------------------------

Go is a tool for managing Go source code.

Usage:

go command [arguments]

The commands are:

build compile packages and dependencies

clean remove object files

env print Go environment information

fix run go tool fix on packages

fmt run gofmt on package sources

get download and install packages and dependencies

install compile and install packages and dependencies

list list packages

run compile and run Go program

test test packages

tool run specified go tool

version print Go version

vet run go tool vet on packages

Use "go help [command]" for more information about a command.

Additional help topics:

c calling between Go and C

gopath GOPATH environment variable

importpath import path syntax

packages description of package lists

testflag description of testing flags

testfunc description of testing functions

Use "go help [topic]" for more information about that topic.