init函数:
每个包中可以包涵任意多个init函数,这些函数会在程序执行开始的时候被调用,init函数会在main函数、设置包、初始化变量或者其它要在程序运行前优先完成的引导工作。
程序编译时,优先执行导入包里的所有init函数,再执行本包内的init函数。
init 和 main 函数
init()、main() 是 go 语言中的保留函数,两个函数在 go 语言中的区别如下:
相同点:
两个函数在定义时不能有任何的参数和返回值
该函数只能由 go 程序自动调用,不可以被引用
不同点:
init 可以应用于任意包中,且可以重复定义多个。
main 函数只能用于 main 包中,且只能定义一个。
两个函数的执行顺序:
对同一个 go 文件的 init( ) 调用顺序是从上到下的
对同一个 package 中的不同文件,将文件名按字符串进行“从小到大”排序,之后顺序调用各文件中的init()函数
对于不同的 package,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 init() 函数
如果 package 存在依赖,调用顺序为最后被依赖的最先被初始化,例如:导入顺序 main –> A –> B –> C,则初始化顺序为 C –> B –> A –> main,一次执行对应的 init 方法。
常量、变量、init()、main()
在同一个文件中,常量、变量、init()、main() 依次进行初始化。
init 顺序
1、在同一个 package 中,可以多个文件中定义 init 方法
2、在同一个 go 文件中,可以重复定义 init 方法
3、在同一个 package 中,不同文件中的 init 方法的执行按照文件名先后执行各个文件中的 init 方法
4、在同一个文件中的多个 init 方法,按照在代码中编写的顺序依次执行不同的 init 方法
5、对于不同的 package,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 init() 函数
6、如果 package 存在依赖,调用顺序为最后被依赖的最先被初始化,例如:导入顺序 main –> A –> B –> C,则初始化顺序为 C –> B –> A –> main,一次执行对应的 init 方法。
所有 init 函数都在同⼀个 goroutine 内执⾏。
所有 init 函数结束后才会执⾏ main.main 函数。
for循环:
go的for循环中不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化变量。
init初始赋值表达式只执行一遍,
循环控制条件、post(给控制变量增量或者减量)每循环一次,都要执行一次。
break语句:
break结束语可以结束for、Switch、select的代码块,break语句还可以在语句后面添加标签,表示退出某个标签对应的代码块。
标签必须要求定义在对应的for、Switch、select的代码块上,标签与对应代码块(for、Switch、select)之间不能有其他代码。
continue语句:
continue语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for循环内使用,在continue语句后添加标签,表示开始标签对应的循环。注意:标签与对应代码块之间不能不能有其他代码。
…语法糖的使用:
1:第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数,如下:
func test1(args …string) {} //可以接受任意个string参数
2:第二个用法是slice可以被打散进行传递,如:
test1(strss…) //切片被打散传入
var strss2= []string{……}
strss=append(strss,strss2…) //strss2的元素被打散一个个append进strss
强制类型转换:
语法格式:var a T = (T)(b)或者var a T = T(b)
转化条件(任意):
1,b可以直接赋值给T类型变量。
2,b类型和T具有相同的底层类型。
3,b类型和T都是整形、浮点型或者复数类型
4,b类型和T都是未命令的指针类型,且指针指向的类型具有相同底层类型。
5,b是整形或者[]byte类型的值,T是string类型
6,b是一个字符串,T是[]byte或者[]rune。
注意:
1、数字跟string类型直接的强转可能会造成值部分丢失,string和数字之间的转换可以使用标准库strconv(strconv.Atoi、Itoa、ParseInt(string,10,64)、FormatInt(int64,10))
2、Go语言没有语言机制支持指针和interget之间的之间转换,可以使用标准库中的unsafe。unsafe.Pointer(&x)
const的定义:
1、只读,不能修改
2、不指定类型,go编译器会自动推导出常量类型。
3、iota每往下一行自增1并赋值给这一行的常量,遇到下一个const重新从0开始。
4、定义多个常量,推荐使用花括号,当然用逗号隔开也是行的。
5,常量未赋值时,往上找最近的有值的常量,并赋值给自己。
布尔类型赋值:
1:直接赋值true和false,默认false
如: var v1 bool v1 = true
2:可以通过比较操作符(<,>, ==,!=, <=, >= )来赋值
如: v2 := (1 == 2) // v2也会被推导为bool类型
3:布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换
var b bool
b = 1 // 编译错误
b = bool(1) // 编译错误
以下的用法才是正确的:
var b bool
b = (1!=0) // 编译正确
go值类型:
包括基本数据类型、数组、结构体
变量声明后,不管是否已经赋值,编译器为其分配内存,值存于栈上,都有默认值,赋值只是值拷贝,可以&获取内存地址
引用类型:
包括指针,slice切片,map,chan,interfere。
变量直接存放的就是一个内存地址,这个地址值指向的空间存的才是值,所以修改一个,另外一个也会修好(同一个内存地址)。引用类型必须申请内存才能使用,make就是给引用类型申请内存空间的。
select的使用:
go中的select 和channel配合使用,用于监控channel的读写io状态,当case中的channel读写操作为非阻塞状态(即能读写),将会触发相应动作。
注意点:
1:select的case必须是个channel操作。
2:case的channel的事件包括:读写的时候,channel被close。
3:如果任意某个通信可以进行,他就执行,其他被忽略。
4、如果有多个case都可以运行,select会随机公平选出一个执行,其他不会执行(包括default也不会执行)。
5、如果没有可运行的case语句,且有default语句,那么就会执行default的动作。
6:如果没有可运行的case语句,且没有default语句,那么select将会阻塞,直到某个case通信可运行。
7、select中所有的channel的读和写都被认为是同时进行的。
典型用法:
1、通过select来检测channel的关闭事件。
2、处理超时。
3、判断channel是否阻塞。
go中的Switch:
1、支持数值、字符串、表达式等复杂计算,默认情况下 case最后自带break语句,成功匹配后不会再执行其他 case。
2、如果需要执行后面的case,可以使用fallthrouth,case带fallthrouth后,程序会继续执行下一条case(下一条如果是default语句,则也会执行),且不会去判断下一个case的表达式是否为true。
3、如果所有的case语句都为false,且有default语句,那么就会执行default语句。如果case都为false,且没有default语句,那么都不会执行(不会阻塞)。
4、Switch条件支持任意类型,case值可以是同类型的任意值,类型必须相同,或最终结果为相同类型的表达式。
可以同时测试多个符合条件的表达式,如: case var1,var2,var3:
Switch语句还可以被用于type-Switch来判断某个interface变量中的实际存储的变量类型,如:
Switch x.(type) {
case type:
……
}
package main
import (
"fmt"
)
func main() {
a := make(map[string]string)
var v interface{}
v = a
switch v.(type) {
case string:
fmt.Println("string")
case int:
fmt.Println("int")
case map[string]string:
fmt.Println(" map[string]string")
}
}