对于go的学习,需要注意的地方。
1、特别注意,go中可以使用go env命令查看我们可以在环境变量中设置的变量。
2、一般的输出使用内建函数print或者println就可以了,fmt.printXXX函数有更多的作用,比如格式化输出
3、
4、系统输出数组喜欢用[]并使用空格作为元素分隔,所以不要以为[]和空格是数据本来就又的,这点需要注意!
5、java以文件学习,因为java中国一个文件对应一个类,而go以模块学习,因为go中都是直接调用模块的函数,而模块的函数都分布在模块下的文件里面。
6、代码块中的:=短式声明在代码块外不起作用,另外,多使用go tool vet -shadow your_fil.go检查隐藏变量。
7、GOBIN环境变量可以让go install的exe文件放置在GOBIN下。
8、函数有多少个返回值,在给变量赋值的时候就需要有多少变量需要来获取这些返回值,但是如果使用range就可以只获取第一个返回值,这点在range可以操作channel就可以看出来了。
9、使用go get url之后,使用import "url"就可以使用这个包了。
10、可以再var()和const()中声明重复的变量,可以再函数中使用:=短式声明多个变量,其中有变量是与上面的变量重复的,这样也是允许的。但是需要注意,在匿名函数中重复声明匿名函数外的变量,并不会影响匿名函数外的变量,这点需要注意。
11、一般使用组合来实现继承关系,所以可以很容易查找到继承的关系,也就可以更好的给函数传值了,一般不会要求通过看函数实现来定位继承关系。
12、实例化可以分为有序初始化和无序初始化。无序初始化:数组{1:“aaa”,3:“bbb”},类xxx{name1:“ddd”,name2:234}
13、变量不可以使用iota,因为常量如果没有被赋值,他会跟上一个常量一个值,而变量不会。
14、append函数给slice增加元素时,如果元素少于1000时,容量按照2倍增加,多于1000时1.5倍增加。
15、使用os.OpenFile设置打开的时候清空目标文件的数据,或者使用File.Truncate函数清除数据都可以用于清楚数据,这里需要注意的是go的清除数据函数使用truncate命名而不是clean或者clear,这是因为truncate可以清除部分数据,这样更加简洁了。
16、go中正则使用regexp模块,使用regexp.complie函数返回一个Regexp对象,使用Regexp.FindXXX函数查找,最后使用FindSubmatch函数可以查找完整匹配和子项匹配,这点跟java的group(n)一样。另外,使用fmt.Printf("%q", )可以格式化输出,特别是像regexp.Split函数返回的[][]byte数组,更加应该通过fmt.Printf("%q", )输出,不然输出的是数字。
17、注意,go中是根据属性来放置容器类的,所以container模块中没有多少文件,而io模块中有pipe文件
18、io.WriteString和ioutil.WriteFile形成的文件是文本格式的,而文本格式会添加东西,所以又出错的可能,而io.Copy函数则不会,因为是根据字节复制的。
19、go的编码包中,一般字符串的编码解码使用Marshal和Unmarshal函数,对于流式读写则使用NewEncoder和NewDecoder函数生成需要的编码解码类,比如对xml,json,gob操作。最后需要注意,go的编码都是utf-8的,所以需要查看操作的编码格式,如果在网页中的话,可以在header的meta中查看charset就能知道编码格式了。
20、windres.exe可以给go的exe程序打包图标,windres.exe在MinGW,tdm-gcc中都可以找到。
21、特别注意,switch后面的表达式可以是任意类型,不限于常量,同时可以先使用初始化语句然后使用“;”分隔后面的表达式。case后面的表达式可以是参数列表例如1,2,3,甚至是值,例如arr[1]。另外,别忘了还可以使用default。
22、在go中,使用T.(X)这种方式实现类型断言,其中T必须是interface{},而X可以是任意类型,甚至世界史type关键字也是可以的,只是如果是type关键字的话就只能在switch中使用了
在go中调用C/C++是非常容易的,可以分为两种:1、可以直接嵌入。在import “C”上使用//或者/**/然后再里面添加需要的例如#include xxx或者代码。这里还可以使用#cgo操作cgo命令,例如#cgo LDFLAGS : -L ./ -lfoo告诉编译器,链接当前目录下lfoo共享库,也就是so文件,如果在编译中使用so库,使用命令go build -ldflags="-r DIR",其中DIR是so库目录。2、在src下创建一个专门存放c代码的文件夹,然后在里面添加h,c,go文件,go文件用于将c,h包装给外面使用。例如下面的例子:
fun.c:
#include <stdio.h> #include <stdlib.h> char* MySecret() { return "my secret not tell anybody"; }
fun.h:
extern char* MySecret();
fun.go
package misc //注意,这里可以使用#include dir来引进某个c库,实际上include就是为了引进文件 //C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt下有我们需要 //的如stdlib.h等文件 // #include "fun.h" import "C" func MySecret() string { return (C.GoString(C.MySecret())); }
网络编程大致上分为b/s,c/s两种。而go中,net实现底层网络功能,net.http实现高级的网络功能。net模块一般用于c/s,而net.http则是c/s,b/s都可以。但是无论是哪种实现方式,其实都是客户端请求数据,而服务端监听并给予数据。
在go中使用net.Dial函数就可以实现客户端的socket功能了,在服务端有net.Listen函数作为服务端的socket功能。这是go的net底层的功能,也是实现c/s的基础方式。
对于常见的方式,应该是使用net.http模块的功能。一个url对应一个网络资源,常见的Get,Post,Put,Delete分别对应增删查改,get查询,post修改,put增加,delete删除。网页的获取方式是get,所以一般get不会被拒绝。这里客户端一般使用net.http.Get()或者net.http.Post()进行数据获取,而服务端使用net.http.ListenAndServe()监听并操作数据,如果是浏览器,其实也是通过net.http.Get()获取数据的,net.http.Post()交互数据的。
对于go的包,不要命名为main,all,std等,不要再src下创建main包,因为在src下创建一个文件并添加package main和main函数就可以作为程序的入口文件了。在引用自己项目的东西的时候,需要使用import "./xxx"而不能是直接import "xxx",不然会在编译的时候出错。这样做其实是为了不和go系统下的文件混淆,当然,如果直接将项目放到GOPATH下的第一个路径,那么可以直接使用import "xxx",因为这个时候,项目被当成系统文件了。
另外,自己的项目模块的命名最好不要跟系统的模块名字一样,因为系统分辨不出来。
最后需要注意,在go项目下点号"."代表的是当前项目路径。
使用go get命令下载并编译的文件会放在GOPATH的第一个路径下,另外,我们的项目也是需要添加到GOPATH下的。最后注意,项目定位到项目名,而不是工作空间名。另外注意,对于需要翻墙的,如果使用cmd,putty,一般网上的代理配置完之后都没用,可以使用git clone直接下载需要翻墙的部分,当然git需要配置代理。然后使用go get下载不用翻墙的部分。其实go get就是使用git clone之后编译下载的项目而已。
当然我们可以使用gopm这个包管理工具,这个工具可以用于安装,搜索,更新和分享go包,所以对于go的第三方包的管理,使用gopm是个不错的选择。
在go中只有不可修复的错误才使用defer,panic(),recover(),其他的用error,一般使用errors.New,fmt.Errorf函数都可以创建实现error接口的错误对象。这里注意,errors本身就是用于操作error的,而fmt则是格式化错误输出。
应该说defer只是延迟操作而已,真正的错误操作是panic和recover函数。其中,panic是保存一个异常信息,因此多次调用panic,当前异常会覆盖上一个异常。其实recover可以在任何地方调用,只是如果没有panic的错误,那么返回的是nil,所以recover只有在defer中调用才能得到异常信息了,而且panic之后程序就返回了。最后需要特别注意,recover需要在defer中直接调用,或者defer后的函数或者匿名函数里面直接调用才可以。不然返回nil,这点可以在进程工作空间看出。
go中有errors,bytes,strings三个模块分别对应error,byte,string的操作封装,应该说go很喜欢使用XXXs来封装对XXX的操作这种命名模式。当然了,XXutil这种也很常见,比如ioutil模块用于辅助io操作。另外,io模块仅仅是常规的io类创建和操作而已,而bufio模块则封装了更多的io操作,特别是返回数据方面,io模块都是字节数组操作的,很不方便,而bufio有更多的操作给我们使用。最后,bufio模块中的scan模块是用于切分的,关键是里面的Scaner类,传参数给Scaner.Split函数,然后调用Scaner.Scan函数产生一次读取扫描,然后可以进行更多的操作。
go中,string和[]byte其实是一样的,只是被定义成了不同的类型,并且string拥有了函数。所以bytes,strings两个模块的函数很多很相似,比如两个模块都有去空格函数。应该特别注意,java中String封装的很多函数,在go中就在strings模块中。同样的bytes封装了[]byte的操作。同时注意,封装了很多很好用的正则函数。最后注意,bytes,strings都封装了相应的Reader,Writer类,和NewReader,NewWriter函数,这也是为了读写操作封装的。
Reader里面的数据读取一次之后就没有了,所以我们可以使用ioutil.FindAll函数得到字节数组,然后将字节数组传递给bytes.NewReader生成Reader,这样就可以再次操作Reader了。
需要注意,bytes.NewBufferString跟java中的StringBuffer一个道理,当然可以使用bytes的Buffer类进行读写。另外,使用fmt.Printf跟strings的操作速度一样,而+的操作更快,最快的是使用bytes.Buffer,bytes.NewBufferString的操作。
go中,最应该掌握的是builtin模块,这个模块里面的都是内建函数,这个模块里面的函数都是用于操作内建数据类型的。比如len函数就是用于得到内建数据类型的数据的长度的。另外,nil的==,!=比较只能是在内置函数中进行,这是因为在go中nil被定义了类型。最后注意,除了指针类型,其他的内置数据类型不能修改指针地址。
go的内建函数copy用于数组间的赋值,可以将string编程[]byte,然后copy之后,将[]byte转化为string,这同时也是strings.Join函数的原理。
说到go的类型,就需要注意type,type用于定义而不是用于别名,type不仅仅可以定义struct,interface,func,还可以用在基本数据类型上。另外,struct用{}初始化,func用()初始化,而interface自身是函数集合。另外,可以在type一个func之后给这个func添加成员func。最后注意,可以type一个func赋值给type的interface,只需要两者有共同的函数,这个过程其实就是type一个func作为函数集合,简单点说就是直接实现函数集合,这比创建一个struct去实例化interface要简单得多。例子如下:
type TestInterface interface {Do()} type FuncDo func() func (self FuncDo) Do() {self()} func main() { var t TestInterface = FuncDo(func() {println("hello world")}) t.Do() }
对于go的第三方包goquery,提供了方便的操作DOM的API,在分析html中非常有用。goquery借鉴了jquery。注意:jquery可以给网页插入元素,添加效果,所以可以不用熟悉js,而通过jquery的脚本编程给网页添加代码,效果。
需要特别注意区分cmd.Args和os.Args,其中,其中cmd.Args是我们去调用命令的时候使用的,而os.Args则是系统调用我们编码的命令程序的时候使用的,所以os.Args[0]可以看到调用的路径,而cmd.Args就是命令的名字,但其实只是os.Args[0]看到的是完整的命令的路径而已,其实如果是我们调用程序,那么os.Args[0]只是输入的第一个字符串而已,系统调用才是完整路径。所以如果是go run就会与路径。os命令行比较麻烦,可以使用flag模块,flag可以直接使用类似go run XX.go -x -xx模式,其中x,xx是自定义参数,同时注意可以使用可以使单横杠“-”,也可以是双横杠“--”。
在go中对于数据类型是需要非常注意的地方,在java中数据类型转换很方便,但是在go中,数据转换不支持隐式转换,所有的转换都需要显式转换。
注意,xxx()可以用于基本数据类型的转换,但是跟string的转换就不行了,当然由于go中string的本质是int的,所以其实string(int)还是可以的。所以关于string跟其他类型的转换需要使用strconv模块进行。总的来说就是,字符串的转换需要使用strconv模块,其他的基本数据类型之间的转换使用XXX()就可以了。
另外注意,查看数据的类型有两种方式:
1、
func t(i interface{}) { //函数t 有一个参数i switch i.(type) { //多选语句switch case string: //是字符时做的事情 case int: //是整数时做的事情 } return }i.(type)只能在switch中使用,所以这种方式局限性比较大。
2、使用Reflect.TypeOf()函数进行判断
未完成
有疑问加站长微信联系(非本文作者)