声明
- 声明是给一个程序实体命名
- 有4个主要的声明
- var:变量
- const:常量
- type:类型
- func:函数
程序员以多个以.go
为后缀的文件里。
- 每一个文件以
package
开头,表明文件属于哪个包 - 后面是
import
声明 - 然后是包级别 的四大声明
变量
var name type = expression
类型和表达式可以省略其一
- 省略类型:则自动根据初始化表达式推断类型
- 省略表达式:自动初始化为该类型对应的0值
- 数字0,布尔false,字符串空串,接口和医用类型是
nil
,符合类型是所有成员零值的集合
- 数字0,布尔false,字符串空串,接口和医用类型是
可以进行列表初始化
var b,f,s = true,2.3,"four"
可以通过调用返回多个值的函数对变量列表进行初始化
var f,err = os.Open(name)
一般在面对返回多个值的函数时,若某返回值不需要,一般使用_
给它占位
var f, _ = os.Open(name)
短变量声明
使用 name:=expression
,直接声明变量,其类型是靠推断得来
指针
指针和C的指针是没啥区别的,不过令人震惊的是竟然可以返回函数中局部变量的指针
func f() *int {
v := 1
return &v
}
new函数
就是c++的new函数,返回某类型的地址
p := new(int)
变量的生命周期
- 包级别变量在整个程序执行期间持续存在
- 局部变量一致生存到其变得 不可访问
逃逸
令人震惊的是,在堆还是在栈上创建变量与是否使用new
无关
比如下面的
var global *int
func f() {
var x int
x = 1
global = &x
}
这里的x在函数调用结束后,其仍能通过global访问到,所以其生命周期不仅仅局限于函数调用期间,我们说这是x逃逸了f,此时x的变量存储在堆上
而
func g() {
y := new(int)
*y = 1
}
该局部变量y在函数调用后就不可达了,所以y的生命周期只在每次调用期间。其被放在栈上
GO的垃圾回收机制保证了其正确性
赋值
使用一个=
代表赋值
多重赋值
在更新左侧变量前,右侧表达式是被同时推演的。
所以可以有令人震惊的下一句
x, y = y, x
即可互换两个变量的值
隐式赋值
- 函数参数
- return
- 复合类型
- map和通道
可赋值性
可赋值性决定了赋值语句的合法不合法
- 类型必须精确匹配
- nil可以赋值给任意接口变量或引用类型
类型声明
type name underlying-type
将某个自己定的名字绑定到某底层类型,就像C里面#define LL long long int ;
通常类型的声明出现在包级别
type Celsius float64
type Fahenheit float64
注意虽然此时虽然两者的底层类型相同,但仍不满足可赋值性的条件
我们可以通过下面的神奇操作将一个函数“关联”到类型上,(就好像是面向对象中的成员函数一样)
func (c Celsius) String() string {
return fmt.Sprinf("%g°C",c)
}
通过在声明后紧跟一个(c Celsius)
使得我们可以这样调用函数c.Strng()
包和文件
一个GO语言项目的样式可能是下面
myproject/
|- main.go
|- config/
| |- config.go
|- controllers/
| |- user_controller.go
|- models/
| |- user.go
|- services/
| |- user_service.go
|- views/
| |- user_view.go
|- utils/
|- utils.go
一个包的源源码保存在一个或多个以.go
结尾的文件中,一个包的所有文件保存在以包名为名字的文件夹内
main是我们的入口,他的前列的声明可能看起来是这样的
package main
import (
"myproject/config"
"myproject/controllers"
)
定义 package
o语言中每个文件的第一行都必须是package声明,它定义了当前文件所属的package。一个package可以由多个文件组成,但是它们必须属于同一个目录。
包级别的变量和函数
- package级别的变量和函数可以被同一个package中的所有文件访问。
- 这些变量和函数可以通过使用package名前缀来访问
导入package
每一个包通过称为导入路径的唯一字符串来标识,就像import ("myproject/config")
,注意包名=文件夹名
导入后我们可以使用别的包中的变量和函数
可导出的变量和函数
这里和C++面向对象继承的东西很类似
- 以大写字母开头的函数和包级别变量(相当于父类的public),在包导入后(类似于继承),可以通过
包名 .名字
使用(可以被子类调用) - 其它的(相当于父类的private),就算被导入(类似于继承),也只能同过原来包的可到出的方法进行访问(只能通过父类的共有方法访问)
包的初始化
有点像类的构造函数,当程序开始运行时,按照包的导入顺序开始初始化(类比面向对象构造函数的调用顺序)
- 有初始化表达式的包级变量先被初始化,按照声明的顺序进行
- 使用任意数量的
func init() {}
进行其它的初始化