Go语言1
随便学学,平时用的都是WIndows10的操作系统,就在这下面搞了。
下载安装下载go安装程序,下载地址:https://golang.org/dl/ (被墙了,打不开)
墙内下载地址http://www.golangtc.com/download
我下的是这个:go1.9.2.windows-amd64.msi
安装就下一步就好了,装完之后验证一下。默认已经把Go的路径加到环境变量中去了,所以可以直接在cmd中输入go命令执行:
装完之后,我装到了D:\Go\,已经帮我们添加好了2个环境变量:
GOROOT: D:\Go\Path: D:\Go\binGOPATH: 这个环境变量也要,但是需要手动设置GOPATH也是要的,设置工作区路径,不过如果没有设置的话,有一个默认位置(差不多就是系统的家目录)。执行帮助命令 go help gopath
,我这截了一段说明:
If the environment variable is unset, GOPATH defaults
to a subdirectory named "go" in the user's home directory
($HOME/go on Unix, %USERPROFILE%\go on Windows),
unless that directory holds a Go distribution.
Run "go env GOPATH" to see the current GOPATH.
暂时不设置也没影响,反正也有默认的,后面写 Hellow World 的时候再看设置在哪个目录
开发工具开发工具先没有装,不过这里先记一下工具的名称:
Visual Studio Code (简称 VS Code / VSC) 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比 Diff、GIT 等特性,支持插件扩展,并针对网页开发和云端应用开发做了优化。软件跨平台支持 Win、Mac 以及 Linux。
这是一个通用的代码编辑器,装完之后要再装一下go的扩展:
点开,菜单-查看>>扩展搜索“go”,找到“Rich Go language support for Visual Studio Code”,作者“lukehoban”,安装装好之后用的时候调试工具Delve是一个非常棒的golang调试工具,支持多种调试方式,直接运行调试,或者attach到一个正在运行中的golang程序,进行调试。
安装Delve非常简单,直接运行 go get 即可:
go get -u github.com/derekparker/delve/cmd/dlv
先记一下,我并没有装
写代码在第一次写代码前,还要先把go的工作区设置好。
设置工作区工作区是存放go源码文件的目录。一般情况,Go源码文件都需要存放到工作区中,不过命令源码文档不是必须的。
每个工作区的目录结构都类似,有3个文件夹:
找一个合适的位置,作为工作区。比如:E:\Go。这时就可以去把GOPATH环境变量设置为 “E:\Go” 。
按上面说的,还要工作区中还要创建3个文件夹,暂时只需要src,另外两个暂时不用。
到这里需要一些仪式感,就是写一个 Hellow World 。在src目录下创建文件 “hellow.go” 。如果用了VS Code,会提示你装一些扩展,点下Install All,这样会自动把一些go的插件给装好。
下面是程序的源码:
package mainimport( "fmt")func main() { fmt.Println("Hello World")}
第一部分,声明这是一个包。可执行文件的包名必须是“main”。
第二部分,导入包。所有的库,包括自己的库,用之前都需要导入。fmt是包名,里面有一个方法可以向终端打印信息。
第三部分,定义了一个函数。必须要定义一个main函数,这是个入口函数。
最后在cmd命令行里运行一下:
go run hello.go
做加法
这次写一个有参数有返回值的函数:
package mainimport( "fmt")func add(a int, b int) int { var sum int sum = a + b return sum}func main(){ var c int c = add(11, 22) fmt.Println("11 + 22 =", c)}
go是一个强类型语言,所有变量都要设置类型。
add函数,写参数的时候,参数后面要用类型。并且由于add函数有返回值,返回值的类型也要在函数名后面定义好。
声明变量用var,后面是变量名,在后面是变量的类型。
最后如果你的变量定义之后,没有被应用,也是会报错的。这样强制你去掉冗余的代码,这个变量既然没有用到,自然就不需要出现,比如把最后一句fmt.Println(c)去掉或者换成打印一个固定的字符串就会报错。
先写一个程序goroute_print.go,很简单,就是传入参数稍微计算一下,然后打印结果:
package mainimport ( "fmt")func goroute_print(a int) { b := a + 10000 fmt.Println(b)}
这里用了 “:=” ,是声明并赋值,并且系统自动推断类型,不需要var关键字。
然后再写一个程序goroute_for.go,里面是一个for循环,起若干个线程执行:
package mainimport( "time")func main(){ for i := 0; i < 1000; i++ { go goroute_print(i) } // sleep1秒,保证上面的子线程运行结束 time.Sleep(time.Second * 5)}
在这个主程序要里调用另外一个文件里的程序。不过这2个文件都是同一个包,main包,开头package声明的。这里在前面加上了go调用,就是起了一个goroute。(不加go调用,就是在主线程里调用函数,如果调用前加上go,就是起了一个子线程调用这个函数)
主线程必须要等待子线程运行结束才能退出,否则主线程一旦结束,其他子线程也就没有了。这里简单粗暴的先用sleep来进行等待。导入了一个time包。参数 time.Second 就是1秒,如果不是等1秒而是几秒的话,就乘以几。
这里还有注释,注释的用法js一样。单行注释以 // 开头,多行注释以 /* 开始,以 */ 结尾。
执行的话在cmd命令行执行下面的命令:
go run goroute_for.go goroute_print.go
这个例子分在两个文件里了,所以run后面把两个文件都要写上。顺序是无所谓的。
管道代码示例这里用了make()函数,是用来分配内存的。第一个参数 chan int
,表示要创建的是chan(管道),类型是int:
package mainimport( "fmt")func main(){ pipe := make(chan int, 3) // 创建一个管道,存的数据类型是int,容量是3 pipe <- 11 // 往管道里存一个数 fmt.Println(11, len(pipe)) // 打印长度 pipe <- 22 fmt.Println(22, len(pipe)) var t1 int t1 =<- pipe // 从管道里取出一个数,赋值给t1 fmt.Println(t1, len(pipe)) t1 =<- pipe fmt.Println(t1, len(pipe))}
似乎就是个队列,先进先出,默认满了也会阻塞。之所以叫管道不叫队列,可能是它能支持多个goroute之间通过channel(管道)进行通信。暂时不需要这么深入。
通过管道在goroute之间传递参数有两种方式可以实现。把管道定义为全局变量,或者把管道作为函数的参数传递。代码中使用全局变量不是好的做法,所以推荐用传参的方法。不过两种实现方法都看一下。
全局变量的代码示例:
package mainimport( "fmt")var pipe chan int // 在全局声明pipe变量,下面两个方法里都会用到func add(a int, b int){ sum := a + b pipe <- sum}func main(){ pipe = make(chan int, 1) go add(1, 2) sum :=<- pipe fmt.Println("sum =", sum)}
传递参数的代码示例:
package mainimport( "fmt")func add(a int, b int, c chan int){ sum := a + b c <- sum}func main(){ var pipe chan int // 在main函数里声明pipe pipe = make(chan int, 1) go add(3, 4, pipe) sum :=<- pipe fmt.Println("sum =", sum)}
多返回值的代码示例
package mainimport( "fmt")func calc(a int, b int)(int, int){ c1 := a + b c2 := (a + b)/2 return c1, c2}func main(){ sum , avg := calc(100, 200) fmt.Println("sum =", sum, "avg =", avg)}
如果多返回值里只想取其中部分的值,可以把剩下的值传给占位符(“_”):
package mainimport( "fmt")func calc(a int, b int)(int, int){ c1 := a + b c2 := (a + b)/2 return c1, c2}func main(){ sum , _ := calc(100, 200) fmt.Println("sum =", sum) fmt.Println(_) // 打印下划线会报错}
尝试打印下划线的报错信息如下:
>go run calc.go# command-line-arguments.\calc.go:17:16: cannot use _ as value
代码格式化(gofmt)
go默认提供一个代码格式化命令 gofmt
,会按照Go语言代码规范格式化文件中的代码。把之前的hello.go的代码搞乱一点,简单一点就改下缩进好了,然后运行下面的命令:
gofmt hello.go
最终还是能再命令行里看到整洁的代码
还截图的效果,缩进是8个字符的位置。
不过原文件里还是乱糟糟的,可以加上-w参数,改写原文件:
gofmt -w hello.go
实际写到文件里,缩进用的不是空格是Tab,具体效果是几个字符就看文件的设置了。
另外换行是 \n
,这个windows的记事本下会用问题,记事本的换行是 \r\n
。
创建Go工作目录:H:\Go\src\go_dev\day1\hello\。把之前写的示例hello.go放到hello目录下。再建个文件夹,其他示例的源文件放到examples目录下。这里src文件夹的名字是不能变的,其他文件夹的名字包括上级的都随意。
上面的演示都没有对go的原文件进行编译,编译要用 go build 命令:
H:\Go>go build go_dev\day1\hellocan't load package: package go_dev/day1/hello: cannot find package "go_dev/day1/hello" in any of: D:\Go\src\go_dev\day1\hello (from $GOROOT) C:\Users\Steed\go\src\go_dev\day1\hello (from $GOPATH)H:\Go>
这里并没有编译成功,原因是找不到我们的源文件。之前因为没设置环境变量GOPATH,所以默认没有设置GOPATH就是把用户家目录作为GOPATH。这个GOPATH就是工作目录。
先去把GOPATH的环境变量设置好,我这里 “H:\Go\“ 就是我的工作目录,设置成:“H:\Go\“ 。建议设置用户变量,当然系统变量也没问题。
完成之后要新开一个cmd,也有可能有要注销当前用户,下次登录才能完全生效。然后可以验证一下:
C:\Users\Steed>go env GOPATHH:\Go\
然后再编译一下,这次随便找个目录执行命令:
J:\tmp>go build go_dev\day1\helloJ:\tmp>hello.exeHello World
命令执行后会在当前目录下生成hello.exe的可执行文件,上面也直接执行了一下。文件名以及生成文件的路径都是可以用参数指定的。
前面只所以不把所有的示例都放一起,是因为其他示例也有main入口函数,存在多个入口函数编译会报错。
其实可以编译单个文件,但是路径要写全(绝对路径或相对路径),单文件编译似乎不受环境变量影响:
J:\tmp>go build H:\Go\src\go_dev\day1\hello\hello.go
复杂一点的程序一般都不是单个文件的,所以一个文件夹里就是一个小项目,只有一个入口函数。这样编译不会有问题。另外如果使用开发工具的话,这种情况开发工具直接也会提示错误的。
最后,下面这么搞比较方便:
H:\Go\src>go build go_dev\day1\helloH:\Go\src>go build go_dev\day1\hello\hello.go
选好cmd的启动路径,之后源文件的路径用Tab就能自动补全出来了。
包的概念代码不能单独存在,任何代码都属于一个包。所以第一行代码,一定写的是package。
把相同功能的代码放到一个目录,称之为包。包可以被其他包调用。main包是用来生成可执行文件的,没个程序只有一个main包包的主要用途是提高代码的可复用性。使用多个包的代码示例新建个文件夹package_exampl,写一个由多个包多个文件构成的代码,具体如下:
// go_dev/day1/package_example/calc/add.gopackage calcfunc Add(x int, y int) int { return x + y}// go_dev/day1/package_example/calc/sub.gopackage calcfunc Sub(x int, y int) int { return x - y}// go_dev/day1/package_example/main/main.gopackage mainimport( // "calc" "go_dev/day1/package_example/calc" "fmt")func main(){ sum := calc.Add(100, 200) sub := calc.Sub(10, 5) fmt.Println(sum, sub)}
calc包里的Add和Sub函数都是首字母大写的。在Go语言里首字母大写有特殊的意义,就是全局变量。如果都小写的话,在main函数里这两个函数,由于作用域的问题,都是不可见的。
结论:就是说一个变量(包括函数),如果要被外部的包调用,必须要首字母大写。不过变量不推荐这么做,因为这样这个变量就是全局变量,而全局变量是不推荐使用的方法(别的语言里也同样是不推荐使用全局变量的)。
main函数里导入包的时候,calc包的路径要写全,不能只写 “calc” ,下面会具体分析。
编译过程
先把import改成这样,一步一步排错:
import( "calc" // "go_dev/day1/package_example/calc" "fmt")
多个包的编译
H:\Go\src>go build go_dev\day1\package_examplecan't load package: package go_dev/day1/package_example: no Go files in H:\Go\src\go_dev\day1\package_example
这里报错,因为文件夹下确实没有包,包的源文件在下一级的目录里:
H:\Go\src>go build go_dev\day1\package_example\maingo_dev\day1\package_example\main\main.go:4:5: cannot find package "calc" in any of: D:\Go\src\calc (from $GOROOT) H:\Go\src\calc (from $GOPATH)
继续报错,这是找不到calc包,并且这里也看到了默认查找包的路径。现在包的路径应该怎么写全就清除了,把import的内容改回来,之后就能顺利的编译执行了:
H:\Go\src>go build go_dev\day1\package_example\mainH:\Go\src>main.exe300 5
指定路径和文件名
一般编译后生成的可执行文件是放在工作目录下的bin目录下的。还是用go build命令,加上-o参数,windows系统下得加上扩展名exe之后才能执行:
H:\Go\src>go build -o ../bin/test.exe go_dev\day1\package_example\mainH:\Go\src>cd ../binH:\Go\bin>test300 5H:\Go\bin>
线程和管道的代码示例
程序分成2部分。一部分是业务逻辑,放在goroute目录下。还有一部分是函数的入口,放在main目录下。文件结构:
H:\GO\SRC\GO_DEV\DAY1\GOROUTE_EXAMPLE├─goroute└─main
业务逻辑就是,获取3个变量,2个数和1个管道,把2个数的和计算后,存到管道里。
主函数里则是,单独起一个线程goroute进行计算,之后从管道里获取到计算结果,打印出来。
这个例子里用到了线程goroute,以及管道用于goroute之间的通信。之前已经演示过了,这里就是把代码分成2个包再写一遍:
// goroute/add.gopackage goroutefunc Add(a int, b int, c chan int){ sum := a + b c <- sum}// goroute/main.gopackage mainimport( "go_dev/day1/goroute_example/goroute" "fmt")func main(){ var pipe chan int pipe = make(chan int, 1) go goroute.Add(100, 200, pipe) res := <- pipe fmt.Println(res) }
官网 Go 指南
都是中文版的。
Go 语言之旅:https://tour.go-zh.org/welcome/1
Go 编程语言:https://go-zh.org/
Go 语言之旅可以作为入门教程,并且可以Web在线写代码,提交执行在Web上显示结果。
使用fmt分别打印字符串、二进制、十进制、十六进制、浮点数。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。