关键字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
变量声明
var关键字:(任意情况都可以使用,一般用来定义全局变量)
var vname1, vname2, vname3 type = v1, v2, v3
简短声明:(仅能用于函数中)
vname1, vname2, vname3 := v1, v2, v3 //编译器自动推导出相应的类型
_ (下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
作用:一般用于接收函数返回得到的后续不使用的值
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
基础数据类型
const cname type = c1
var active bool = true
var c complex64 = 5+5i
var s string = "hello"
s[0] = 'c'
错误类型
Go内置有一个error 类型,专门用来处理错误信息,Go的package 里面还专门有一个包errors 来处理错误:
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
iota枚举
默认开始值是0,const中每增加一行加1:
package main
import (
"fmt"
)
const (
x = iota
y = iota
z = iota
w
)
const v = iota
const (
h, i, j = iota, iota, iota
)
const (
a = iota
b = "B"
c = iota
d, e, f = iota, iota, iota
g = iota
)
func main() {
fmt.Println(a, b, c, d, e, f, g, h, i, j, x, y, z, w, v)
}
array
【注意】数组之间赋值为值传递
var arr [n]type
a := [3]int{1, 2, 3}
b := [10]int{1, 2, 3}
c := [...]int{4, 5, 6}
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
slice
【注意】slice为引用类型
var fslice []int
slice := []byte {'a', 'b', 'c', 'd'}
slice 可以从一个数组或一个已经存在的slice 中再次声明。slice 通过array[i:j] 来获取,其中i 是数组的开始位置,j 是结束位置,但不包含array[j] ,它的长度是j-i 。
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
var a, b []byte
a = ar[2:5]
b = ar[3:5]
注意slice 和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用... 自动计算长度,而声明slice 时,方括号内没有任何字符。
slice 是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值。
对于slice 有几个有用的内置函数:
-
len 获取slice 的长度
-
cap 获取slice 的最大容量
-
append 向slice 里面追加一个或者多个元素,然后返回一个增加元素后的slice
-
copy 函数从源slice 的src 中复制元素到目标dst ,并且返回复制的元素的个数
注:append 函数会改变slice 所引用的数组的内容,从而影响到引用同一数组的其它slice 。 但当slice 中没有剩余空间(即(cap-len) == 0 )时,此时将动态分配新的数组空间。返回的slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice 则不受影响。
package main
import "fmt"
func main() {
array := [...]int{1,2,3,4,5,6,7}
s := array[2:4]
a := append(s, 6)
fmt.Println(array, s, a)
}
从Go1.2开始slice支持了三个参数的slice,之前我们一直采用这种方式在slice或者array基础上来获取一个slice
var array [10]int
slice := array[2:4]
这个例子里面slice的容量是8,新版本里面可以指定这个容量
slice = array[2:4:7]
上面这个的容量就是7-2 ,即5。这样这个产生的新的slice就没办法访问最后的三个元素。
如果slice是这样的形式array[:i:j] ,即第一个参数为空,默认值就是0。
map
【注意】map也是引用类型
map 也就是Python中字典的概念,它的格式为map[keyType]valueType
var numbers map[string]int
numbers = make(map[string]int)
numbers["one"] = 1
numbers["ten"] = 10
numbers["three"] = 3
fmt.Println("第三个数字是: ", numbers["three"])
通过delete 删除map 的元素:
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C")
上面说过了,map 也是一种引用类型,如果两个map 同时指向一个底层,那么一个改变,另一个也相应的改变:
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut"
make、new操作
make 用于内建类型(map 、slice 和channel )的内存分配。new 用于各种类型的内存分配。
内建函数new 本质上说跟其它语言中的同名函数功能一样:new(T) 分配了零值填充的T 类型的内存空间,并且返回其地址,即一个*T 类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T 的零值。有一点非常重要:
new 返回指针。
内建函数make(T, args) 与new(T) 有着不同的功能,make只能创建slice 、map 和channel ,并且返回一个有初始值(非零)的T 类型,而不是*T ,args 包括了len 长度和cap 容量。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice ,是一个包含指向数据(内部array )的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice 为nil 。对于slice 、map 和channel 来说,make 初始化了内部的数据结构,填充适当的值。
make 返回初始化后的(非零)值。
区别:
package main
import "fmt"
func main() {
var p *[]int = new([]int)
(*p)[0] = 1
fmt.Println((*p)[0], (*p)[1])
var s []int = make([]int, 3)
s[0] = 2
fmt.Println(s[0], s[1])
}
原因:
利用new 来分配slice 结构,但是结构中的应该指向底层数组的ptr 指针为空,故不能向这个slice 里面存取数据;利用make 来分配slice 结构,此时结构中应该只想底层数组的ptr 指针已经指向了某个底层数组。这个底层数组已经分配了,所以可以使用。
func
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
return value1, value2
}
变参
func myfunc(arg ...int) {}
函数作为参数
package main
import "fmt"
type testInt func(int) bool
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd)
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even)
}
函数内部常用语法:defer,panic,recover
defer–延迟语句
可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。 (有些类似于析构函数)
例子:
func ReadWrite() bool {
file.Open("file")
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
我们看到上面有很多重复的代码,Go的defer 有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在defer 后指定的函数会在函数退出前调用。
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
panic
是一个内建函数,可以中断原有的控制流程,进入一个panic 状态中。当函数F 调用panic ,函数F的执行被中断,但是F 中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F 的行为就像调用了panic 。这一过程继续向上,直到发生panic 的goroutine 中所有调用的函数返回,此时程序退出。panic 可以直接调用panic 产生。也可以由运行时错误产生,例如访问越界的数组。
下面这个函数演示了如何在过程中使用panic
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
}
recover
是一个内建的函数,可以让进入panic 状态的goroutine 恢复过来。recover 仅在延迟函数中有效。在正常的执行过程中,调用recover 会返回nil ,并且没有其它任何效果。如果当前的goroutine 陷入panic 状态,调用recover 可以捕获到panic 的输入值,并且恢复正常的执行。
下面这个函数检查作为其参数的函数在执行时是否会产生panic :
func throwsPanic(f func()) (b bool) {
defer func() {
if x := recover(); x != nil {
b = true
}
}()
f()
return
}
main函数和init函数
init 函数(能够应用于所有的package )和main 函数(只能应用于package main )。这两个函数在定义时不能有任何的参数和返回值
import
fmt是Go语言的标准库,其实是去GOROOT 环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
-
相对路径
import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
-
绝对路径
import “shorturl/model” //加载gopath/src/shorturl/model模块
上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事
-
点操作
我们有时候会看到如下的方式导入包
import(
. "fmt"
)
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello world”)可以省略的写成Println(“hello world”)
-
别名操作
别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
import(
f "fmt"
)
别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println(“hello world”)
-
_操作
这个操作经常是让很多人费解的一个操作符,请看下面这个import
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
|
请发表评论