go语法和特点零碎总结 - Silentdoer

go语法和特点零碎总结

1.go里通过首字母大小写来区分它是私有的还是公有的,比如对于一个结构体属性一般就以大写开头(和Java不一样,不需要什么getter,setter方法);而对于方法而言,它是隶属于包(包名一定是小写的),因此大写表示它可以被包外访问,小写只能被包内访问;

还能声明在函数里,这种情况只对函数局部可见;

2.如果要声明一个变量但不初始化,必须指定类型,如var obj AnClassType;(常量用const,类型是type,函数是func),如果同时初始化可以用短变量的方式如obj := new (AnClass),注意,这里和其他语言的new AnClass()不一样;

3.1.13版本的go,string转换为字节数组,默认就是UTF-8的;

4.struct也是值类型,因此struct的变量不能和nil比较,而必须是&AStructObj;

5.如果方法返回时出现异常,则其值为默认值,如int是0,struct类型是一个{};

6.对于特殊名字,如JSON, HTML,则一般这样命名HTMLEscape()或htmlEscape()而不会叫escapeHtml();

7.对于普通方法定义一般是func met() (ret int, err error) {... ret = 3;return},只要需要返回返回值或异常其中的一种则最后就必须要有return,但是和Java等语言不一样的是不需要写return xxx,因为它就是返回函数签名里的ret和err;

而对于扩展函数则是这么写func (o AType) extMet() {..},这个写法和接口的实现写法一致,可以理解为扩展方法其实就是实现匿名的接口;注意AType不能是基础类型或基础类型别名,如int,但是可以是基础类型原型的其他类型,如type AType = int;

8.go里没有继承的说法,比如type BType AType{...}是不允许的,但是可以用组合,type Btype {AType},然后里面可以添加其他属性,这样可以很好的解决Java里PO,DTO之类的问题,不过注意,虽然BType里可以调用AType的所有属性,但是BType实例

不能赋值给AType类型变量;

9.go里的常用工具类一般是type名字的复数,如strings,errors等等;

10.如果不是涉及到多态,那其实用扩展函数就可以,但是要实现接口变量承载多个类型实例,则需要接口来实现;

11.go处理配置文件的工具为:github.com/spf13/viper,即viper,可以处理JSON、TOML、YAML、HCL和Java properties

12.go里的new一个结构体是new (StructType)【当然,这里new其实是一个内置函数,比如len和print和println等都是内置函数】,而不是像Java里new类一样的new AClass(),然后强制转换也是不一样,go里是 AType(o),而Java里一般是(AType) o,这里是go比较好,Java里如果转换后还要做操作一般还得再套一层括号。

13.go里日志框架可以用logrus;

14.go里可以用短变量声明来声明新变量(必须有新变量,多重赋值里也是至少存在一个新变量才能用短变量声明),即类似js里全局变量的声明方式,即不需要指定var、const,也不需要指定类型如 ob := obj;,ob必须是新的变量;

15.go里可以用多重赋值,如a, b, c = 1, 2, 3;有点像短变量赋值,这些变量也是可以不预先声明,如果值是表达式比较复杂最好分开声明;多重赋值有点像元组;

16.go安装好后最好设置这几个环境变量,一个是GOPATH,它代表工作目录(类似Eclipse的工作空间),可以有多个用英文逗号分隔;它不仅仅是用来存放自己写的代码,自己引用的第三方源码也是存储在这里的(但是go mod后就可以不用非得把项目建立在GOPATH里的目录中,但是它还是有一个作用就是go mod下载的依赖文件是存储在$GOPATH/pkg/mod里)【如果没有配置则默认是~/go(其实就可以用默认的即可)】;第二个是GOROOT,它代表go的安装目录,对于一些IDE可能会需要检测这个环境变量才能自动识别"gdk";第三个是GO111MODULE=on开启go mod支持(1.13之后应该是默认就是开启了?);

17.全局里(函数外)不能有简式声明(a := 8),但是可以这样var a int = 8【或者var a = 8】;然后全局里和函数内对变量的声明(不包括赋值)都是var b int这种形式;而且注意,同一作用域里如果已经存在var a int的声明,则不能在后续用a := 9这样的简式声明(类似变量重复定义);

但是如果var a int是全局的(相对),则局部里可以用a := 8,在局部作用域里会用局部简式声明创建的变量;【如果要同时声明多个变量,可以var (...)来实现,每个变量占一行】

18.简式声明允许平行赋值,即a, b := 3, "数据库",这样a是3,b是"数据库"

19.golang里的切片有点类似Java里的ArrayList;从数组里创建一个切片可以有三个参数,一个是切片第一个元素在数组里的下标,第二个是切片的length,第三个是capacity;比如var arr = [4]int {1, 2, 3, 4,5};

然后var s = arr[1:2:3]表示s这个切片的第一个元素是arr[1],然后它的length是2,所以可以直接s[1]访问得出值3,然后容量是3,所以s2 := append(s, 8)后s2的length是3,s1[2]会报错,而s2[2]不会,且s2[2]值为8,而此时arr[3]的值也被修改为了8;如果继续append,由于capacity满了,所以这个时候会开辟新的连续地址来存放切片(即go不是发现容量不够了才开辟,而是会比较如果下一次再append会超过容量的话就会提前开辟新的空间;开辟的容量会是len(s) + 2),这个时候再改变新的切片的元素不会影响到arr了;

20.golang可以对slice进行重组(其实就类似subSlice,但又不是),比如slice := []int{1, 2, 3}【这里假设其cap为5】,那么可以通过slice2 := slice[0:1]来获取一个基于slice的新的切片,0和1都是下标的意思,且是左闭右开,如果左下标忽略则默认是0,即slice[:1],然后这里要注意右下标小于等于slice的长度即可(虽然可以大于长度,但不建议这么做);

21.全局(包)作用域里不能有简式声明,即name := "sfjkl"是错误的,但是可以var name = "sfjkl"或var name string = "sfkjl"(右值也可以是一个函数返回string);也不行先var name string然后换行name = "sfjlk";

22.go里const修饰的标识符是常量而非只读变量,即可以const name = "fsjdklj",但是不可以const name = test("sfjkl")

23.go里所有的类型都实现了空接口interface{},包括基础类型,类似java里Object的升级版

24.go里面的接口实现和方法添加类似C#里的扩展方法,接口可以导入接口所在的包然后为某个类型实现它,但是注意,为类型A实现接口或者添加方法要求实现方法的代码和定义类型A的是同一个包,因此无法为基础类型扩展方法或实现接口;

25.go里的包不像java一样可以有.,即aa目录对应一个package aa,里面的所有.go文件都应该属于aa这个包,然后aa目录下有bb目录,bb目录下的.go文件都属于bb这个包,这里只能是package bb而无法package aa.bb,所以感觉和Java比起来层次感少了。。

和Python的模块很像,这种方式很容易出现包命名冲突,所以需要用到包别名(包名规范里不建议用下划线和复数);

26.go的包里的全局变量如果是大写开头也是可以导出的,它和类型名,属性名一样遵守导出规则;但是main包里定义的全局变量和类型应该用小写开头,因为它们不需要被外部包主动获取,但是属性可以是大写开头,特别是那种需要被json库之类的解析的;(不过其实类型名也可以大写开头,除非是那种确实一定不希望外部用到的类型才用小写开头,比如临时类型用于计算,计算后会转成其他类型给外部提供的,那么这个临时类型就该用小写开头)

27.go里包其实就类似Java中的一个类,而全局变量和函数则是这个“包类”的属性和方法;在包内部定义的结构体类似java里的内部类的概念;

同理Python和JS的模块则类似是一个全局方法(同时又具备“包类”的功能,外部可以通过包名来访问全局函数里定义的变量或类型【不过js需要export】)【不要被以往的概念束缚,有的语言方法里也可以定义类型】,或者这么理解好一点,就是Python和JS的模块省略了类似go的init方法,把他直接弄到全局里去了(init方法可以理解为“包类”的静态块,在这个“包类”加载的时候就会执行),所以Python和JS的模块整个代码都是这个“模块类”的静态块;

28.golang里文件名是下划线分割,而类型名,变量名,属性/字段名则是驼峰命名(包括大驼峰,小驼峰),包名则是全小写(如果包名过长可以采用缩写,如fsnotify),包名不应该有复数;常量则是全大写用下划线分隔多个单词;接口命名一般以er或able结尾或以I开头;变量命令还有一点特殊:

和结构体类似,变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写,
但遇到特有名词时,需要遵循以下规则:
如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient
其它情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID
错误示例:UrlArray,应该写成 urlArray 或者 URLArray
若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头

29.go里面有函数类型,如type HandlerFunc func(ResponseWriter, *Request),实例化一个HandlerFunc变量可以通过赋值给这个函数类型变量一个匿名或具名函数,如var kk HandlerFunc = func(..., ...){},也可以直接var s = HandlerFunc(func(...,...){});

而且注意HandlerFunc变量可以赋值给和它具备相同函数签名的接口,如var h Handler = kk,所以函数类型变量其实也是一个实现了所有有该函数签名(除了函数名,毕竟函数类型不需要定义函数名,所以匹配任意函数名)的一个对象;

30.go通过fmt.Sprint(..)实现java里toString()的功能

31.go的管道子类型如果是error,interface这类的值可以是nil的类型,那么是可以往管道里发nil数据的,且接收器也能接收到,收到的值就是nil

32.go build时可以加上 -ldflags \'-s\' 使得build的可执行文件不包含debug信息,这样可以令二进制文件小很多。

33.可以在build前临时设置环境变量来实现交叉编译,如GOOS=windows GOARCH=amd64 go build main.go,支持的操作系统类型和处理器架构包括:

const goosList = "aix android darwin dragonfly freebsd hurd illumos js linux nacl netbsd openbsd plan9 solaris windows zos "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "

34.go语言里string和java的String不一样,go的string默认值/零值是空字符串,且不能是nil,而java里的String的值可以是空字符串也能是null;

35.同一个目录里不能有两个不同的包(可以多个.go文件然后用相同的包,包名是可以和所在目录名不一样,但是除了根目录其他的最好是一致的);