Initialization
Although it doesn't look superficially very different from initialization in C or C++, initialization in Go is more powerful. Complex structures can be built during initialization and the ordering issues between initialized objects in different packages are handled correctly.
从表面上看 Go的初始化和C/C++区别不大 但是Go更加给力 复杂的数据结构可以在初始化的时候建立起来 并且Go可以准确地处理不同包之间的对象初始化顺序
Constants 常量初始化
Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.
Go中的常量在编译的时候创建 即使变量是定义在函数内的局部变量 也是在编译过程中搞定 常量只可以是数字 字符串 或者布尔值 受限于这个条件 常量表达式必须是可以被编译器推导出的 举例来说1<<3 是常量表达式 而math.Sin(math.Pi/4)就不是常量表达式 因为这里涉及到了函数调用 这个是在运行时才进行计算的
In Go, enumerated constants are created using the iota enumerator. Since iota can be part of an expression and expressions can be implicitly repeated, it is easy to build intricate sets of values.
Go中的枚举常量 可以通过iota来创建 由于iota可以是表达式的一部分 而且表达式可以被重复 可以很容易地创建复杂的数据集 每一个const关键字出现时 iota会被重置为0 iota会在下一次引用时自动+1 当const赋值表达式相同时可省略之后的赋值表达式 下面的这个例子的赋值表达式就省略了 统一为 1<<(10*iota)
type ByteSize float64
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
The ability to attach a method such as String to a type makes it possible for such values to format themselves automatically for printing, even as part of a general type.
Go中可以为大多数类型定义方法 比如为某个类型定义String方法 就可以输出改类型的字符串表达形式
func (b ByteSize) String() string { switch { case b >= YB: return fmt.Sprintf("%.2fYB", b/YB) case b >= ZB: return fmt.Sprintf("%.2fZB", b/ZB) case b >= EB: return fmt.Sprintf("%.2fEB", b/EB) case b >= PB: return fmt.Sprintf("%.2fPB", b/PB) case b >= TB: return fmt.Sprintf("%.2fTB", b/TB) case b >= GB: return fmt.Sprintf("%.2fGB", b/GB) case b >= MB: return fmt.Sprintf("%.2fMB", b/MB) case b >= KB: return fmt.Sprintf("%.2fKB", b/KB) } return fmt.Sprintf("%.2fB", b) }
The expression YB prints as 1.00YB, while ByteSize(1e13) prints as 9.09TB.
通过这个函数 YB可以打印成1.00YB, 而ByteSize(1e13)则打印成9.09TB
Note that it's fine to call Sprintf and friends in the implementation of String methods, but beware of recurring into the String method through the nested Sprintf call using a string format (%s, %q, %v, %x or %X). The ByteSize implementation of String is safe because it calls Sprintf with %f.
在写String函数的时候 可以调用Sprintf以及相关的输出函数 但是要注意防止Sprintf使用格式化修饰符%s, %q, %v, %x, %X, 这里ByteSize的String方法是安全的 因为只用了%f修饰符
Variables 变量初始化
Variables can be initialized just like constants but the initializer can be a general expression computed at run time.
变量可以想常量一样被初始化 但是也可以通过一般的表达式 在运行时再对其初始化
var ( home = os.Getenv("HOME") user = os.Getenv("USER") goRoot = os.Getenv("GOROOT") )
The init function init初始化函数
Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.
每个源文件都可以定义自己的init初始化函数(可以有多个init函数)init会在所有包中 变量初始化完成后调用 这个概念可以和其它面向对象的构造函数做对比 最特征化的子类 最后才被初始化 而父类最先被初始化
Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.
init的通常用在 程序真正执行前 验证并且修复程序的状态
func init() { if user == "" { log.Fatal("$USER not set") } if home == "" { home = "/home/" + user } if goRoot == "" { goRoot = home + "/go" } // goRoot may be overridden by --goroot flag on command line. flag.StringVar(&goRoot, "goroot", goRoot, "Go root directory") }
有疑问加站长微信联系(非本文作者)