go语言中可以通过类型申明或者使用关键字make和new来定义类型,然后系统会分配内存来存放所定义类型的值。在没有给定义的类型变量初始化的时候,内存会给定一个对应类型的默认零值。例如booleans类型的默认零值为false,Integer类型为0,floats类型为0.0,string类型为"",指针、函数、接口、切片slice、channel和map类型其零值为nil。
这种始终为申明的变化量赋初值的特点对程序的安全性和正确性是有帮助的,同时也使得go程序更简单和紧凑。
如下一个例子是使用sync.Mutex包,在没有显示的初始化情况下还是可以正常使用。sync.Mutex包含两个不可导出的整数属性。不管sync.Mutex怎么被申明的,这两个属性值会赋为默认零值。
package main
import "sync"
type MyInt struct {
mu sync.Mutex
val int
}
func main() {
var i MyInt
i.mu.Lock() // i.mu没有被显示初始化,但是还是可用
i.val++
i.mu.Unlock()
}
上述代码中,sync.Mutex本身就是一个结构体包含两个整数,如下所示:
type Mutex struct {
state int32
sema uint32
}
尽管在我们的代码中没有被显示申明,但是在定义i变量的时候,结构体重的每个字段都会被初始化为默认零值(0)。
使用默认零值的另一个例子是bytes.Buffer。你可以申明一个bytes.Buffer就可以执行读取和写入功能而不需要显示初始化。注意:io.copy函数第二个参数为io.Reader,需要传入一个指针。如下所示:
package main
import (
"bytes"
"io"
"os"
)
func main() {
var b bytes.Buffer
b.Write([]byte("Hello world"))
io.Copy(os.Stdout, &b)
}
以上代码其实,在定义b变量的时候会初始化一个[]byte字节切片来存放内容:
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
}
切片的一个非常有用的特性就是其零值是nil。这意味着你不需要显示的使用make来创建一个切片,只需要申明即可:
package main
import (
"fmt"
"strings"
)
func main() {
//s := make([]string, 0)
//s := []string{}
var s []string
s = append(s, "Hello")
s = append(s, "World")
fmt.Println(strings.Join(s, " "))
}
var s []string和注释的两行类似,但是不完全一样。使用make定义其长度零值为0,而直接var定义是nil。可以通过如下代码验证:
package main
import (
"fmt"
"reflect"
)
func main() {
s0 := make([]string, 0)
s1 := []string{}
var s2 []string
fmt.Println(reflect.DeepEqual(s0, s1)) //都是显示申明结果为ture
fmt.Println(reflect.DeepEqual(s1, s2))//结果为false
}
类型值为nil指针还有一个用途,就是可以调用对应类型的方法。这使得无需初始化就可以调用类型的方法。
如下所以:
package main
import (
"fmt"
)
type Config struct {
path string
}
func (c *Config)Path() string {
if c == nil{
return "/usr/home"
}
return c.path
}
func main() {
var c1 *Config
var c2 = &Config{
"/export",
}
fmt.Println(c1.Path(), c2.Path())
}
output: /usr/home /export