读书笔记源于:GO web 编程
src 存放源代码(比如:.go .c .h .s等)
pkg 编译后生成的文件(比如:.a)
bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中)
如果是main包,当你执行go build之后,它就会在当前目录下生成一个可执行文件。如果不是main包则无反应
这个命令是用来移除当前源码包里面编译生成的文件
go fmt <文件名>.go : 用于格式化代码
使用go fmt命令,更多时候是用gofmt,而且需要参数-w,否则格式化结果不会写入文件。gofmt -w src,可以格式化整个项目。
实际上分成了两步操作:第一步是下载源码包,第二步是执行go install。
go get github.com/astaxie/beedb 可以下载远程包
go get -u 参数可以自动更新包
为了go get 能正常工作,你必须确保安装了合适的源码管理工具
这个命令在内部实际上分成了两步操作:第一步是生成结果文件(可执行文件或者.a包),第二步会把编译好的结果移到 G O P A T H / p k g 或 者 GOPATH/pkg或者 GOPATH/pkg或者GOPATH/bin。
执行这个命令,会自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件。
如何查看相应package的文档呢? 例如builtin包,那么执行go doc builtin 如果是http包,那么执行go doc net/http 。查看某一个包里面的函数,那么执行godoc fmt Printf 。也可以查看相应的代码,执行godoc -src fmt Printf。
通过命令在命令行执行 godoc -http=:端口号 比如godoc -http=:8080。然后在浏览器中打开127.0.0.1:8080,你将会看到一个golang.org的本地copy版本,通过它你可以查询pkg文档等其它内容。如果你设置了GOPATH,在pkg分类下,不但会列出标准包的文档,还会列出你本地GOPATH中所有项目的相关文档,这对于经常被墙的用户来说是一个不错的选择。
go fix 用来修复以前老版本的代码到新版本,例如go1之前老版本的代码转化到go1
go version 查看go当前的版本
go env 查看当前go的环境变量
go list 列出当前全部安装的package
go run 编译并运行Go程序
以上这些工具还有很多参数没有一一介绍,用户可以使用go help 命令获取更详细的帮助信息。
vim的使用参考:vim 学习
我的Linux机器是腾讯云提供的主机,安装的是Ubuntu16.04
vim 的配置主要有两个地方,一个是 /etc/vim/文件夹下(有一个vimrc文件,这个文件名不是.开头的),这是全局配置。一个是个人主目录下( ~/.vimrc(需要自己创建文件), ~/.viminfo),这是用户配置。
使用命令go get -u github.com/nsf/gocode
下载自动补全工具(安装教程)执行如下命令:
~ cd $GOPATH/src/github.com/nsf/gocode/vim
~ ./update.bash(这个脚本是在主目录下创建.vim文件夹存放插件)
~ gocode set propose-builtins true(是否自动提示Go的内置函数、类型和常量,默认为false,不提示。)
然后在~/.vimrc
文件中加入filetype plugin on
即可,这个命令是让脚本生效。没有这个命令在执行自动补全时可能会报option ‘omnifunc’ is notset
的错误。(这里真是傻了,以为要执行source vimrc
,其实不用,source是用于执行bashrc文件变更时用的,如果用source执行这个vimrc文件会报错filetype: command not found
)
在~/.vimrc
文件下加入如下几行,即可实现括号的自动补全
inoremap ( ()<ESC>i
inoremap [ []<ESC>i
inoremap { {}<ESC>i
inoremap < <><ESC>i
var variableName type
var vname1, vname2, vname3 type
var variableName type = value
var vname1, vname2, vname3 type= v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3
vname1, vname2, vname3 := v1, v2, v3
:=这个符号直接取代了var和type,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用var方式来定义全局变量。
_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
Go对于已声明但未使用的变量会在编译阶段报错
const Pi float32 = 3.1415926
const prefix = "astaxie_"
bool
int uint(长度取决于编译器的实现)
rune, int8, int16, int32, int64和byte, uint8, uint16,uint32, uint64。其中rune是int32的别称,byte是uint8的别称。
这些类型的变量之间不允许互相赋值或操作,不然会在编译时引起编译器报错。int 与 int32并不可以互用。
浮点数的类型有float32和float64两种(没有float类型),默认是float64。
复数
string 在Go中字符串是不可变的.Go中可以使用+操作符来连接两个字符串,字符串也可以使用切片操作。
string类型的变量包含两个字段,Data中存放的是字符串实际存放位置 的地址,string的不可变性就是指 Data字段指向的位置是一个不可修改的内存区域。但string变量本身的 Data和Len字段的值是可以改变的。
s := "hello"
c := []byte(s) // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s2 := string(c) // 再转换回 string 类型
fmt.Printf("%s\n", s2)
m := `hello
world` (Raw字符串)
Go内置有一个error类型,专门用来处理错误信息,Go的package里面还专门有一个包errors来处理错误
primes := [4]int{2,,3,5,7} //数组
const(
i = 100
pi = 3.1415
prefix = "Go_"
) //分组声明
除非被显式设置为其它值或iota,每个const分组的第一个常量被默认设置为它的0值,第二及后续的常量被默认设置为它前面那个常量的值,如果前面那个常量的值是iota,则它也被设置为iota。
Go里面有一个关键字iota,这个关键字用来声明enum的时候采用,它默认开始值是0,每调用一次加1:
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用
)
const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0
当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针
var arr [10]int // 声明了一个int类型的数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
在Go里面的动态数组叫slice,是一个引用类型,指向一个底层array,声明slice时,方括号内没有任何字符。
var fslice []int
slice := []byte {'a', 'b', 'c', 'd'}
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
var a, b []byte
a = ar[2:5]
当引用改变其中元素的值时,其它的所有引用都会改变该值
arr := [5]int{2,3,4,3,7}
testSlice := arr[:]
arr[0] = 90
fmt.Printf("%v", testSlice[0]) //输出结果值是90
从底层理解,一个slice可以看出一个包含指针,长度,最大容量共3个成员的结构体。
对于slice有几个有用的内置函数:
len 获取slice的长度
cap 获取slice的最大容量
append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice
copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数
注:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
map是无序的,每次打印出来的map都会不一样,是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应的改变
var numbers map[string] int
numbers := make(map[string]int)
// map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
csharpRating, ok := rating["C#"]
delete(rating, "C") // 删除key为C的元素
m1 := m //m和m1的改变会互相影响,因为是引用类型
new(T) 为一个 T 类型新值分配空间并将此空间初始化为 T 的零值,返回的是新值的地址,也就是 T 类型的指针 *T,该指针指向 T 的新分配的零值。
make 只能用于 slice,map,channel 三种类型,make(T, args) 返回的是初始化之后的 T 类型的值,这个新值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。
slice 的零值是 nil,使用 make 之后 slice 是一个初始化的 slice,即 slice 的长度、容量、底层指向的 array 都被 make 完成初始化,此时 slice 内容被类型 int 的零值填充,形式是 [0 0 0],map 和 channel 也是类似的。
// 计算获取值x,然后根据x返回的大小,判断是否大于10。
if x := computedValue(); x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
} /
/这个地方如果这样调用就编译出错了,因为x是条件里面的变量
fmt.Println(x)
for index:=0; index < 10 ; index++ {
sum += index
}
//for配合range可以用于读取slice和map的数据:
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
//由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_来丢弃不需要的返回值 例如
for _, v := range map{
fmt.Println("map's val:", v)
}
i := 10
switch i {
case 1:
fmt.Println("i is equal to 1")
case 2, 3, 4:
fmt.Println("i is equal to 2, 3 or 4")
case 10:
fmt.Println("i is equal to 10")
default:
fmt.Println("All I know is that i is an integer")
}
//Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。
func max(a, b int) int {
if a > b {
return a
}
return b
}
//变参,arg是一个slice
func myfunc(arg ...int) {
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
}
Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。
func ReadWrite() bool {
file.Open("file")
// 做一些工作
if failureX {
file.Close()
return false
} i
f failureY {
file.Close()
return false
} f
ile.Close()
return true
}
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
} i
f failureY {
return false
} r
eturn true
}
type testInt func(int) bool,可以把这个类型的函数当做值来传递
Panic和Recover
main函数和init函数
//可以省略前缀的包名
import(
. "fmt"
)
//别名操作
import(
f "fmt"
)
//_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
type person struct {
name string
age int
}
var P person
P := person{"Tom", 25}
P := person{age:24, name:"Tom"}
当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct,就像访问自己所有用的字段一样。不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // 匿名字段,那么默认Student就包含了Human的所有字段
speciality string
}
//也可以整体赋值
mark.Human = Human{"Marcus", 55, 220}
GO使用最外层的优先访问,允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。
func (r ReceiverType) funcName(parameters) (results)
如果一个method的receiver是T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method
类似的如果一个method的receiver是T,你可以在一个T类型的变量P上面调用这个method,而不需要 *P去调用这个method
method也是可以继承的。如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method。
一个对象可以实现任意多个interface,任意的类型都实现了空interface
如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。
空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是可以通过定义interface参数,让函数接受各种类型的参数。fmt.Println就是可以接受任何实现了String方法的参数。
Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。
反射机制
go say("world")
就是开一个新的Goroutines执行
设计上我们要遵循:不要通过共享来通信,而要通过通信来共享
runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。
channels 来做通讯(包括超时,缓冲等…)