go语言相比于Java多了指针的使用,因此在变量的使用上,更加灵活。但是巧妙的使用能避免内存的浪费!
二、三种方式 var c1 *Config
var c2 = &Config{}
c3 := new(Config)
三、区别
上面的三种方式返回的都是指针,但是又有什么区别呢?
fmt.Println(reflect.TypeOf(c1),reflect.TypeOf(c2),reflect.TypeOf(c3))
打印结果
*main.Config *main.Config *main.Config
可以见的三种返回的值都是指针,好像并没有什么不同,那接下来我们进行赋值,看看会不会有什么不同?
c1.path = "./go"
c2.path = "./go"
c3.path = "./go"
此时运行你会发现,c1赋值的时候竟然报错了,c2,c3输出的结果一样
那么c1为什么报错呢。我们不赋值来打印一下c1,c2,c3
<nil>
&{ 0}
&{ 0}
c1为空指针,c2,c2都被赋予初始值
此时结果就已经很明显了,c1只是声明一个指针。c2通过结构体{}赋予初始值。c3通过new也进行了初始值的赋值。因此c1不能赋值,c2,c3可以进行赋值。
那么c1,c2c3究竟在什么时候使用呢?
四、不同场景下的使用我们先定义一个函数
func (c *Config) Path() string {
if c == nil {
return "/usr/home"
}
return "/go"
}
然后我们分别用c1,c2,c3调用,分析返回值有什么不同?
/home
./go
./go
发现都能调用成功,且返回值不同,c2和c3貌似一直都没有区别,那我们就重点分析c1和c3
还是回到那个问题,什么时候用c1和什么时候用c3呢?
五、需要用到变量进行赋值的时候使用刚刚我们定义了一个函数,在里面我们并没有使用结构体里面的变量,我们重新写这个函数
func (c *Config) Path() string {
if c == nil {
return "/home"
}
c.path = "./go"
return c.path
}
这个时候如果我们没加c==nil的条件判断,此时用c1调用就会报空指针错误。
在函数中我们使用了c.path,因此我们常常在声明的时候使用c3:=new(Config)
如果在函数中不适用,则完全可以使用c1,这种方式只声明一个指针,更加节省空间。
每次我们定义指针也不知道在接下来的函数中是否使用,或则上游传递给调用下游的函数,也不知道下游是否使用,遇到这种情况我们怎么办呢?是全部用new给结构体赋上初始值,还是使用一个单纯的指针呢,如果单纯的使用指针,报空指针这个锅谁来背?
这个时候我们就可以使用懒加载。既节省内存,又不担心报错。
func (c *Config) Path() string {
if c == nil {
c = &Config{}
}
c.path = "./go"
return c.path
}
经过我们简单的改动,上面代码c1,c2,c3结果相同,也不会报空指针异常。
其中c==nil的判断,然后赋值就是懒加载的过程。
懒加载:当我们使用的时候才给他开辟内存空间,而不是一开始就给他开辟一块内存,并赋予初始值。