《The Way to Go》之1 [4.2.1 包的概念、导入与可见性]《读书笔记系列》
学习某个语言后,忽然停用个半年的,很容易就忘记。又要重新学习!
所以为了快速 学习+回顾 golang,做了这个概要整理
4.2.1 包的概念、导入与可见性
你必须在源文件中非注释的第一行指明这个文件属于哪个包
如:package main。 package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
包名只能是 小写字母
如果对一个包进行更改或重新编译,所有引用了这个包的客户端程序都必须全部重新编译。
导入包的写法
//method 1
import "fmt"
import "os"
//method 2
import "fmt";import "os"
//method 3
import ("fmt"; "os")
//method 4
import (
"fmt"
"os"
)
可见性规则
-
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,
如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public); -
标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
Group1 (首字母 大写 ,作用域 public )
group1 (首字母 小写 ,作用域 private )
注意事项
- 如果你导入了一个包却没有使用它,则会在构建程序时引发错误,如 imported and not used: os,这正是遵循了 Go 的格言:“没有不必要的代码!“。
包的分级声明和初始化
- 你可以在使用 import 导入包之后定义或声明 0 个或多个常量(const)、变量(var)和类型(type),这些对象的作用域都是全局的(在本包范围内),所以可以被本包中所有的函数调用(如 gotemplate.go 源文件中的 c 和 v),然后声明一个或多个函数(func)。
变量定义
type IZ int
type (
IZ int
FZ float64
STR string
)
var a IZ = 5
var obj type = xxx
var
4.2.2 函数
左大括号 { 必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会出现错误提示:
build-error: syntax error: unexpected semicolon or newline before {
Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误
也可以写成一行
func Sum(a, b int) int { return a + b }
对于大括号 {} 的使用规则在任何时候都是相同的(如:if 语句等)。
因此符合规范的函数一般写成如下的形式:
func functionName(parameter_list) (return_value_list) {
…
}
- parameter_list 的形式为 (param1 type1, param2 type2, …)
- return_value_list 的形式为 (ret1 type1, ret2 type2, …)
只有当某个函数需要被外部包调用的时候才使用大写字母开头(就是前文 提到的 public),并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。
下面这一行调用了 `fmt` 包中的 `Println` 函数,可以将字符串输出到控制台,并在最后自动增加换行字符 `\n`:
```go
fmt.Println("hello, workd")
```
使用 `fmt.Print("hello, world\n")` 可以得到相同的结果。
Notice Print 不会自动添加\n换行字符,需要自己添加
4.2.3 注释
go文本格式,默认UTF8
// 单行注释。
/* */ 多行注释
godoc 工具(第 3.6 节)会收集这些注释并产生一个技术文档。
4.2.4 类型
可以包含数据的变量(或常量)可以使用不同的数据类型或类型来保存数据。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。
类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。
结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。
函数也可以是一个确定的类型,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如:
func FunctionName (a typea, b typeb) typeFunc
你可以在函数体中的某处返回使用类型为 typeFunc 的变量 var:
return var
一个函数可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号 ()
将它们括起来,如:
func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
示例: 函数 Atoi (第 4.7 节):func Atoi(s string) (i int, err error)
返回的形式:
return var1, var2
这种多返回值一般用于判断某个函数是否执行成功(true/false)或与其它返回值一同返回错误消息(详见之后的并行赋值)。
使用 type 关键字可以定义你自己的类型(类似c/c++ 的typedef),你可能想要定义一个结构体(第 10 章),但是也可以定义一个已经存在的类型的别名,如:
type IZ int
这里并不是真正意义上的别名,因为使用这种方法定义之后的类型可以拥有更多的特性,且在类型转换时必须显式转换。
然后我们可以使用下面的方式声明变量:
var a IZ = 5
这里我们可以看到 int 是变量 a 的底层类型,这也使得它们之间存在相互转换的可能(第 4.2.6 节)。
如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如:
type (
IZ int
FZ float64
STR string
)
每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),因为 Go 语言是一种静态类型语言。
4.2.5 Go 程序的一般结构
下面的程序可以被顺利编译但什么都做不了,不过这很好地展示了一个 Go 程序的首选结构。这种结构并没有被强制要求,编译器也不关心 main 函数在前还是变量的声明在前,但使用统一的结构能够在从上至下阅读 Go 代码时有更好的体验。
所有的结构将在这一章或接下来的章节中进一步地解释说明,但总体思路如下:
- 在完成包的 import 之后,开始对常量、变量和类型的定义或声明。
- 如果存在 init 函数的话,则对该函数进行定义(这是一个特殊的函数,每个含有该函数的包都会首先执行这个函数)。
- 如果当前包是 main 包,则定义 main 函数。
- 然后定义其余的函数,首先是类型的方法,接着是按照 main 函数中先后调用的顺序来定义相关函数,如果有很多函数,则可以按照字母顺序来进行排序。
示例 4.4 gotemplate.go
package main
import (
"fmt"
)
const c = "C"
var v int = 5
type T struct{}
func init() { // initialization of package
}
func main() {
var a int
Func1()
// ...
fmt.Println(a)
}
func (t T) Method1() {
//...
}
func Func1() { // exported function Func1
//...
}
Go 程序的执行(程序启动)顺序如下:
- 按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程:
- 如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。
- 然后以相反的顺序在每个包中初始化常量和变量,如果该包含有 init 函数的话,则调用该函数。
- 在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。
4.2.6 类型转换
在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数):
valueOfTypeB = typeB(valueOfTypeA)
类型 B 的值 = 类型 B(类型 A 的值)
示例:
a := 5.0
b := int(a)
但这只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(例如将 int16 转换为 int32)。当从一个取值范围较大的转换到取值范围较小的类型时(例如将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。当编译器捕捉到非法的类型转换时会引发编译时错误,否则将引发运行时错误。
具有相同底层类型的变量之间可以相互转换:
var a IZ = 5
c := int(a)
d := IZ(c)
4.2.7 Go 命名规范
干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。Go 语言中对象的命名也应该是简洁且有意义的。像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码的可读性。名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符。返回某个对象的函数或方法的名称一般都是使用名词,没有 Get...
之类的字符,如果是用于修改某个对象,则使用 SetName
。有必须要的话可以使用大小写混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下划线来分割多个名称。
以下是代码
// cmpout
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test that we can do page 1 of the C book.
package main
import (
"fmt"
"runtime"
)
//自定义类型
type (
IZ int
FZ float64
STR string
)
//特殊的函数,每个含有该函数的包都会首先执行这个函数
func init(){
fmt.Print("enter init() \n")
fmt.Printf("go version ==> %s \n", runtime.Version())//类似 POSIX 的printf
var a IZ = 100
var b FZ = 3.14
var c STR = "justString"
fmt.Print(a,b,c)//Print() 不会自动添加 回车&空格
/*
//如果2个类型 都是 数值类型,写在一起,会自动添加了空格
fmt.Print(a,a,b,a,b,b,a,a)
//分开写 没有添加空格
fmt.Print(a)
fmt.Print(b)
*/
//fmt.Println(234)
fmt.Println("==分隔符=====")
//Println()自动添加 空格
fmt.Println(a,b,c)
//Println()自动添加 换行符
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Print("leave init() \n")
}
func main() {
var val int32 //初始值 为0
fmt.Println(val)
a := 123;//自动根据值 来匹配类型
var b int32 = 223;//显式 赋值
fmt.Println(a,b)
//必须显式 转换类型
var a1 IZ = 5
b1 := int(a1)
c1 := IZ(b1)
fmt.Println(a1, b1,c1)
fmt.Println( funcA( int32(a), b) )// a 由int显式转换成int32, 各个
}
func funcA(a int32, b int32)(ret1 int32, ret2 int32){
//fmt.Println("enter funA ", a,b)
//fmt.Println(a+b)
return a+1,b+1
}