[TOC]

工程组织

$GOPATH 必须采用和 $GOROOT 不一样的值 ,其中$GOROOT 是go的安装目录,$GOPATH是我们进行编写代码的工作目录,这个路径下又必须分别包含三个规定的目录:srcpkg 和 bin,这三个目录分别用于存放源码文件、包文件和可执行文件。

$GOPATH 可以包含多个工作目录,取决于你的个人情况。如果你设置了多个工作目录,那么当你在之后使用 go get(远程包安装命令)时远程包将会被安装在第一个目录下。

命令工具

  • go build 编译并安装自身包和依赖包
  • go install 安装自身包和依赖包
  • 在命令行输入 gofmt –w program.go 会格式化该源文件的代码然后将格式化后的代码覆盖原始内容(如果不加参数 -w 则只会打印格式化后的结果而不重写文件);gofmt -w *.go 会格式化并重写所有 Go 源文件;gofmt map1 会格式化并重写 map1 目录及其子目录下的所有 Go 源文件。
  • go fix 用于将你的 Go 代码从旧的发行版迁移到最新的发行版,它主要负责简单的、重复的、枯燥无味的修改工作,如果像 API 等复杂的函数修改,工具则会给出文件名和代码行数的提示以便让开发人员快速定位并升级代码。Go 开发团队一般也使用这个工具升级 Go 内置工具以及 谷歌内部项目的代码。go fix 之所以能够正常工作是因为 Go 在标准库就提供生成抽象语法树和通过抽象语法树对代码进行还原的功能。该工具会尝试更新当前目录下的所有 Go 源文件,并在完成代码更新后在控制台输出相关的文件名称。

代码风格

  1. 不以分号结束。(其实由编译器完成,所以定义函数的{要在定义首行出现,否则会编译错误)

  2. 使用//或/**/注释。

  3. 反斜杠\可以当做多行连接符使用。

每个文件都属于且仅属于一个包。

一个包可以由多个.go文件组成,文件名和包名一般不一样。

必须在每个文件的第一行指明所属的包。

package main代表一个独立可执行的文件,每个应用必须包含一个main的包。

引入多个包的方式:

import "fmt"
import "os"
//或者
import "fmt"; import "os"
//或者
import (
   "fmt"
   "os"
)

当包名为fmt,会直接在全局文件中查找。当有./或/会在相对路径或绝对路径查找。

可见性原则:

当包内标识符(变量、常量、结构体、函数、类型等)大写开头时,外部包可用,称为导出。当为小写时,包内可见。

使用别名:

import fm "fmt"

Import with . :

import . "./pack1"

当使用.来做为包的别名时,你可以不通过包名来使用其中的项目。例如:test := ReturnStr()

在当前的命名空间导入 pack1 包,一般是为了具有更好的测试效果。

Import with _ :

import _ "./pack1/pack1"

pack1包只导入其副作用,也就是说,只执行它的init函数并初始化其中的全局变量。

导入外部安装包:

如果你要在你的应用中使用一个或多个外部包,首先你必须使用 go install

函数

​ 每个可执行程序都应该包含main函数,它没参数也没返回值。

func functionName(parameterlist) (returnvalue_list) {
   
}

init函数

变量除了可以在全局声明中初始化,也可以在 init 函数中初始化。这是一类非常特殊的函数,它不能够被人为调用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。

每个源文件都只能包含一个 init 函数。

传递长参数

如果函数的最后一个参数是采用 ...type 的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变参函数。

func myFunc(a, b, arg ...int) {}

示例函数和调用:

func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")

在 Greeting 函数中,变量 who 的值为 []string{"Joe", "Anna", "Eileen"}

defer和追踪

关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。

package main

import "fmt"

func main() {
	doDBOperations()
}

func connectToDB() {
	fmt.Println("ok, connected to db")
}

func disconnectFromDB() {
	fmt.Println("ok, disconnected from db")
}

func doDBOperations() {
	connectToDB()
	fmt.Println("Defering the database disconnect.")
	defer disconnectFromDB() //function called here with defer
	fmt.Println("Doing some DB operations ...")
	fmt.Println("Oops! some crash or network error ...")
	fmt.Println("Returning from function here!")
	return //terminate the program
	// deferred function executed here just before actually returning, even if
	// there is a return or abnormal termination before
}

内置函数

Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。

名称 说明
close 用于管道通信
len、cap len 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
new、make new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针(详见第 10.1 节)。它也可以被用于基本类型:v := new(int)。make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作。new() 是一个函数,不要忘记它的括号
copy、append 用于复制和连接切片
panic、recover 两者均用于错误处理机制
print、println 底层打印函数,在部署环境中建议使用 fmt 包
complex、real imag 用于创建和操作复数

函数当做参数

函数可以作为其它函数的参数进行传递,然后在其它函数内调用执行,一般称之为回调。

package main

import (
	"fmt"
)

func main() {
	callback(1, Add)
}

func Add(a, b int) {
	fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
}

func callback(y int, f func(int, int)) {
	f(y, 2) // this becomes Add(1, 2)
}

计算函数执行时间

start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)

常量

常量定义格式:

const identifier [type] = value

在go中可以省略type,因为它可以根据值来推断。

const Pi = 3.14159
const beef, two, c = "eat", 2, "veg"
const Monday, Tuesday, Wednesday, Thursday, Friday, Saturday = 1, 2, 3, 4, 5, 6
const (
	Monday, Tuesday, Wednesday = 1, 2, 3
	Thursday, Friday, Saturday = 4, 5, 6
)

还可以用作枚举:

const (
	a = iota
	b = iota
	c = iota
)

//或简写成

const (
	a = iota //0
	b //1
	c //2
)

每次遇到const关键字就会重置iota为0

变量

变量申明格式:

var identifier type

声明:

var a, b *int
//or
var a int
var b bool
var str string
//or
var (
	a int
	b bool
	str string
)

当声明完后,系统自动赋予变量该类型的0值。

一般命名使用小驼峰,如果需要被外部包引用,使用大驼峰。

一般函数外申明的变量为全局变量,可以在全局和包内外使用。函数内定义的或者控制结构代码块内定义的,为局部变量。

变量赋值可以在编译期间或者运行时进行。

a = 15
b = false
//or
var identifier [type] = value
var a int = 15
var i = 5
var b bool = false
var str string = "Go says hello to the world!"
//or
var a = 15
var b = false
var str = "Go says hello to the world!"
//or
var (
	a = 15
	b = false
	str = "Go says hello to the world!"
	numShips = 50
	city string
)

//or

a, b, c := 1, false, "str"

当在函数内部声明局部变量时应使用a:=1这种简明方式。

值引用:

  1. 值类型进行赋值时,在内存里进行了复制。值类型的变量的值保存在栈中。

  2. 可以使用&i来获取i的内存地址。这个内存地址被称为指针。

  3. _变量是只写变量,不能读,用来存抛弃值。

数字

布尔类型,bool,true和false。

整型,

  • int 在32位系统使用32位,64位系统使用64位。

  • uint 无符号。

  • uintptr 长度被设计成足够存放一个指针。

  • int8,int16,int32,int64 长度分别为8位、16位等,所以能表示的数大小也因此改变。

  • uint8,uint16,uint32,uint64

    浮点型,

  • float32 精确到小数点后7位

  • float64 精确到小数点后15位,一般使用这个

  • 不同类型的数值之间不能进行运算,需要首先统一进行显式转换,比如m=int32(n)

  • byte类型是int8的别名,表示ASCII码,也可用int来表示UTF-8,一般加\u来表示,var a int = ‘\u0041’

字符串

  1. 是值类型,且值不可变。字符串是字节的定长数组。

  2. 字符拼接符+

  3. strings包提供了常见的字符统计拼接截取等方法。strconv提供了类型转换方法。

指针

取值的指针&

定义指针

var a *int

取指针的值\,比如 \a

不能得到文字或者常量的指针

控制结构

if-else:

if condition1 {
	// do something

} else if condition2 {
	// do something else
}else {
	// catch-all or default
}

//先赋值
if value := process(data); value > max {
	...
}
//常用
if _, err := cRds.Do("AUTH", "73c5cca127891432"); err != nil {
    log.Fatal(err)
}

switch:

switch num1 {
case 98, 99: //多值判断
	fmt.Println("It's equal to 98")
case 100:
	fmt.Println("It's equal to 100")
default:
	fmt.Println("It's not equal to 98 or 100")
}

//条件判断
switch {
	case i < 0: f1() case i == 0: f2() case i > 0:
		f3()
}
//fallthrough,执行完后再执行下面分支
switch k {
	case 4: fmt.Println("was <= 4"); fallthrough;
	case 5: fmt.Println("was <= 5"); fallthrough;
	case 6: fmt.Println("was <= 6"); fallthrough;
	case 7: fmt.Println("was <= 7"); fallthrough;
	case 8: fmt.Println("was <= 8"); fallthrough;
	default: fmt.Println("default case")
}

for:

for i := 0; i < 5; i++ {
	fmt.Printf("This is the %d iteration\n", i)

}
//类似while
for i >= 0 {
		i = i - 1
		fmt.Printf("The variable i is now: %d\n", i)
	}
//类似foreach
for pos, char := range str {
	...
}

for-range可以遍历任意集合(数组,map,字符串)