- Go安装
三种安装方式,源码安装、压缩包安装
- GOPATH和工作空间
作用?工作空间的目录结构
GO命令
- go build
- 如果是普通包,就像我们在1.2节中编写的mymath包那样,当你执行go build之后,它不会产生任何文件。如果你需要在$GOPATH/pkg下生成相应的文件,那就得执行go install。
- 如果是main包,当你执行go build之后,它就会在当前目录下生成一个可执行文件。如果你需要在$GOPATH/bin下生成相应的文件,需要执行go install,或者使用go build -o 路径/a.exe。
- 你也可以指定编译输出的文件名。例如1.2节中的mathapp应用,我们可以指定go build -o astaxie.exe,默认情况是你的package名(非main包),或者是第一个源文件的文件名(main包)。
(注:实际上,package名在Go语言规范中指代码中“package”后使用的名称,此名称可以与文件夹名不同。默认生成的可执行文件名是文件夹名。)
- go build会忽略目录下以“_”或“.”开头的go文件。
- 如果你的源代码针对不同的操作系统需要不同的处理,那么你可以根据不同的操作系统后缀来命名文件。例如有一个读取数组的程序,它对于不同的操作系统可能有如下几个源文件:
array_linux.go array_darwin.go array_windows.go array_freebsd.go go build的时候会选择性地编译以系统名结尾的文件(Linux、Darwin、Windows、Freebsd)。例如Linux系统下面编译只会选择array_linux.go文件,其它系统命名后缀文件全部忽略。 参数的介绍
-o 指定输出的文件名,可以带上路径,例如 go build -o a/b/c
-i 安装相应的包,编译+go install
-a 更新全部已经是最新的包的,但是对标准包不适用
-n 把需要执行的编译命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的
-p n 指定可以并行可运行的编译数目,默认是CPU数目
-race 开启编译的时候自动检测数据竞争的情况,目前只支持64位的机器
-v 打印出来我们正在编译的包名
-work 打印出来编译时候的临时文件夹名称,并且如果已经存在的话就不要删除
-x 打印出来执行的命令,其实就是和-n 的结果类似,只是这个会执行
-ccflags 'arg list' 传递参数给5c, 6c, 8c 调用
-compiler name 指定相应的编译器,gccgo还是gc
-gccgoflags 'arg list' 传递参数给gccgo编译连接调用
-gcflags 'arg list' 传递参数给5g, 6g, 8g 调用
-installsuffix suffix 为了和默认的安装包区别开来,采用这个前缀来重新安装那些依赖的包,-race 的时候默认已经是-installsuffix race ,大家可以通过-n 命令来验证
-ldflags 'flag list' 传递参数给5l, 6l, 8l 调用
-tags 'tag list' 设置在编译的时候可以适配的那些tag,详细的tag限制参考里面的 Build Constraints
- go clean 这个命令是用来移除当前源码包和关联源码包里面编译生成的文件。
- go fmt 代码格式化
用go fmt命令,其实是调用了gofmt,而且需要参数-w,否则格式化结果不会写入文件。gofmt -w -l src,可以格式化整个项目。 所以go fmt是gofmt的上层一个包装的命令,我们想要更多的个性化的格式化可以参考 gofmt gofmt的参数介绍
- -w 把改写后的内容直接写入到文件中,而不是作为结果打印到标准输出。
- -r 添加形如“a[b:len(a)] -> a[b:]”的重写规则,方便我们做批量替换
- -s 简化文件中的代码
- -d 显示格式化前后的diff而不是写入文件,默认是false
- -e 打印所有的语法错误到标准输出。如果不使用此标记,则只会打印不同行的前10个错误。
- -cpuprofile 支持调试模式,写入相应的cpufile到指定的文件
- go get 这个命令是用来动态获取远程代码包的
这个命令在内部实际上分成了两步操作:第一步是下载源码包,第二步是执行go install。下载源码包的go工具会自动根据不同的域名调用不同的源码工具, 参数 -d 之只下载不安装
- go install 这个命令在内部实际上分成了两步操作:第一步是生成结果文件(可执行文件或者.a包),第二步会把编译好的结果移到$GOPATH/pkg或者$GOPATH/bin。
- go test 执行这个命令,会自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件。
- go tool 下面下载聚集了很多命令
- go generate 这个命令是从Go1.4开始才设计的,用于在编译前自动化生成某类代码。
- godoc 在Go1.2版本之前还支持go doc命令,但是之后全部已到了godoc这个命令下,需要这样安装go get
go version 查看go当前的版本 go env 查看当前go的环境变量 go list 列出当前全部安装的package go run 编译并运行Go程序
开发工具
- Sublime Text
- PHPStorm
- LiteIDE
Go语言基础
Go使用package(和Python的模块类似)来组织代码。main.main()函数(这个函数位于主包)是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者),所以它天生支持多语言。
基础知识
- 定义变量
定义一个名称为“variableName”,类型为”type”的变量 var variableName type 定义三个类型都是“type”的变量var vname1, vname2, vname3 type 初始化“variableName”的变量为“value”值,类型是“type” var variableName type = value 定义三个类型都是”type”的变量,并且分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 var vname1, vname2, vname3 type= v1, v2, v3 定义三个变量,它们分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 编译器会根据初始化的值自动推导出相应的类型 vname1, vname2, vname3 := v1, v2, v3 流程和函数流程控制
if Go里面if条件判断语句中不需要括号,如下代码所示
1 2 3 4 5
| if x > 10 { fmt.Println("x is greater than 10") } else { fmt.Println("x is less than 10") }
|
goto 用goto跳转到必须在当前函数内定义的标签。
1 2 3 4 5 6 7
| func myFunc() { i := 0 Here: //这行的第一个词,以冒号结束作为标签 println(i) i++ goto Here //跳转到Here去 }
|
for Go里面最强大的一个控制逻辑就是for,它即可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下:
1 2 3
| for expression1; expression2; expression3 { //... }
|
expression1、expression2和expression3都是表达式,其中expression1和expression3是变量声明或者函数调用返回值之类的,expression2是用来条件判断,expression1在循环开始之前调用,expression3在每轮循环结束之时调用。 通常用法
1 2 3 4 5 6 7 8 9 10 11
| package main import "fmt" func main(){ sum := 0; for index:=0; index < 10 ; index++ { sum += index } fmt.Println("sum is equal to ", sum) } // 输出:sum is equal to 45
|
循环
1 2 3 4
| sum := 1 for ; sum < 1000; { sum += sum }
|
简写
1 2 3 4
| sum := 1 for sum < 1000 { sum += sum }
|
for 中有break 、continue
1 2 3 4 5 6 7 8
| for index := 10; index>0; index-- { if index == 5{ break // 或者continue } fmt.Println(index) } // break打印出来10、9、8、7、6 // continue打印出来10、9、8、7、6、4、3、2、1
|
for配合range可以用于读取slice和map的数据:
1 2 3 4
| for k,v:=range map { fmt.Println("map's key:",k) fmt.Println("map's val:",v) }
|
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_来丢弃不需要的返回值 例如
1 2 3
| for _, v := range map{ fmt.Println("map's val:", v) }
|
switch if 不够用使用switch 1 2 3 4 5 6 7 8 9 10
| switch sExpr { case expr1: some instructions case expr2: some other instructions case expr3: some other instructions default: other code }
|
another
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| integer := 6 switch integer { case 4: fmt.Println("The integer was <= 4") fallthrough case 5: fmt.Println("The integer was <= 5") fallthrough case 6: fmt.Println("The integer was <= 6") fallthrough case 7: fmt.Println("The integer was <= 7") fallthrough case 8: fmt.Println("The integer was <= 8") fallthrough default: fmt.Println("default case") }
|
函数函数是Go里面的核心设计,它通过关键字func来声明,它的格式如下:
1 2 3 4 5
| func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { //这里是处理逻辑代码 //返回多个值 return value1, value2 }
|
关键字func用来声明一个函数funcName 函数可以有一个或者多个参数,每个参数后面带有类型,通过,分隔 函数可以返回多个值 上面返回值声明了两个变量output1和output2,如果你不想声明也可以,直接就两个类型 如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号 如果没有返回值,那么就直接省略最后的返回信息 如果有返回值, 那么必须在函数的外层添加return语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main import "fmt" // 返回a、b中最大值. func max(a, b int) int { if a > b { return a } return b } func main() { x := 3 y := 4 z := 5 max_xy := max(x, y) //调用函数max(x, y) max_xz := max(x, z) //调用函数max(x, z) fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它 }
|
多个返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main import "fmt" //返回 A+B 和 A*B func SumAndProduct(A, B int) (int, int) { return A+B, A*B } func main() { x := 3 y := 4 xPLUSy, xTIMESy := SumAndProduct(x, y) fmt.Printf("%d + %d = %d\n", x, y, xPLUSy) fmt.Printf("%d * %d = %d\n", x, y, xTIMESy) }
|
变参 func myfunc(arg …int) {} 传值与传指针
当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。 传指针使得多个函数能操作同一个对象。 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。 Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)
defer Panic和Recover
struct类型
Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型person代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之struct。如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type person struct { name string age int } type person struct { name string age int } var P person // P现在就是person类型的变量了 P.name = "Astaxie" // 赋值"Astaxie"给P的name属性. P.age = 25 // 赋值"25"给变量P的age属性 fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
|
除了上面这种P的声明使用之外,还有另外几种声明使用方式:
- 1.按照顺序提供初始化值
P := person{“Tom”, 25}
- 2.通过
field:value 的方式初始化,这样可以任意顺序 P := person{age:24, name:”Tom”}
- 3.当然也可以通过
new 函数分配一个指针,此处P的类型为*person P := new(person)
面向对象带有接收者的函数,我们称为method 计算面积的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| //原始的函数 package main import "fmt" type Rectangle struct { width, height float64 } func area(r Rectangle) float64 { return r.width*r.height } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} fmt.Println("Area of r1 is: ", area(r1)) fmt.Println("Area of r2 is: ", area(r2)) } //接受者 type Rectangle struct { width, height float64 } type Circle struct { radius float64 } func (r Rectangle) area() float64 { return r.width*r.height } func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) }
|
method继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string } type Employee struct { Human //匿名字段 company string } //在human上面定义了一个method func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() }
|
interface
Go语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便 简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为。
并发
goroutine goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main import ( "fmt" "runtime" ) func say(s string) { for i := 0; i < 5; i++ { runtime.Gosched() fmt.Println(s) } } func main() { go say("world") //开一个新的Goroutines执行 say("hello") //当前Goroutines执行 }
|
runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine
channels
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:
1 2 3 4 5 6
| ci := make(chan int) cs := make(chan string) cf := make(chan interface{}) //channel通过操作符<-来接收和发送数据 ch <- v // 发送v到channel ch. v := <-ch // 从ch中接收数据,并赋值给v
|
channel例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main import "fmt" func sum(a []int, c chan int) { total := 0 for _, v := range a { total += v } c <- total // send total to c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x + y) }
|
Buffered Channels
1 2 3 4 5 6 7 8 9 10 11 12 13
| package main import "fmt" func main() { c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行 c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } //修改为1报如下的错误: //fatal error: all goroutines are asleep - deadlock!
|
web基础
- web工作原理
- http包建立Web服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package main import ( "fmt" "net/http" "strings" "log" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析参数,默认是不会解析的 fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息 fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) fmt.Println(r.Form["url_long"]) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, "")) } fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的 } func main() { http.HandleFunc("/", sayhelloName) //设置访问的路由 err := http.ListenAndServe(":9090", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } }
|
|