关键字var,而类型信息放在变量名之后,变量声明语句不需要使用分号作为结束符。示例如下:
var v1 int
var v2 string
var v3 [10]int // 数组
var v4 []int // 数组切片
var v5 struct {
f int
}
var v6 *int // 指针
var v7 map[string]int // map,key为string类型,value为int类型
var v8 func(a int) int
var (
v1 int
v2 string
)
var v1 int = 10 // 正确的使用方式1
var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型
v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型
错误写法:
var i int
i := 2
var v10 int
v10 = 123
i, j = j, i //支持变量直接交换
func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"
}
_, _, nickName := GetName() //_接受返回内容,但无法使用
常量无类型,比如常量-12,它可以赋值给int、uint、int32、int64、float32、float64、complex64、complex128等类型的变量。
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", 无类型整型和字符串常量
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota在每个const开头被重设为0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0
v float64 = iota * 42 // v == 42.0
w = iota * 42 // w == 84
)
const x = iota // x == 0 (因为iota又被重设为0了)
const y = iota // y == 0 (同上)
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <<iota // a == 1 (iota在每个const开头被重设为0)
b // b == 2
c // c == 4
)
go语言没有类似java的枚举,只有常量,例如:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)
布尔类型:bool。
整型:int8、byte、int16、int、uint、uintptr等。
浮点类型:float32、float64。
复数类型:complex64、complex128。
字符串:string。
字符类型:rune。
错误类型:error。
指针(pointer)
数组(array)
切片(slice)
字典(map)
通道(chan)
结构体(struct)
接口(interface)
布尔类型:不能接受其他类型的赋值,不支持自动或强制的类型转换:
var b bool
b = 1 // 编译错误
b = bool(1) // 编译错误
整形:分为int ,int8,int16,int32,int64 和无符号的uint ,uint8,uint16,uint32,uint64
int和int32在Go语言里被认为是两种不同的类型,不能相互赋值,不能相互比较,但和常量可以赋值和比较:
var value2 int32
value1 := 64 // value1将会被自动推导为int类型
value2 = value1 // 编译错误
value2 = int32(value1) // 通过强制转换后可以赋值,编译通过
var i int32
var j int64
i, j = 1, 2
if i == j { // 编译错误
fmt.Println("i and j are equal.")
}
if i == 1 || j == 2 { // 编译通过
fmt.Println("i and j are equal.")
}
位运算:Go语言支持位运算。左移,右移,异或,与,或,取反
x << y 左移 等于乘2
x >> y 右移 等于除以2
浮点型:Go语言定义了两个类型float32和float64。默认不带小数点的数值被自动推断为整形,带小数点的数被自动推导为float64.
浮点数不是一种精确的表达方式,不能直接==比较,推荐的替代方案:
import "math"
// p为用户自定义的比较精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。
对于一个复数z = complex(x, y),就可以通过Go语言内置函数real(z)获得该复数的实
部,也就是x,通过imag(z)获得该复数的虚部,也就是y。
字符串的内容可以用类似于数组下标的方式获取,但与数组不同,字符串的内容不能在初始
化后被修改:
var str string // 声明一个字符串变量
str = "Hello world" // 字符串赋值
ch := str[0] // 取字符串的第一个字符
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
str := "Hello world" // 字符串也支持声明时进行初始化的做法
str[0] = 'X' // 编译错误
字符串操作有:x + y 字符串连接,len(s) 字符串长度,s[i] 取字符
Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历,一种是是以Unicode字符遍历
str := "Hello,世界"
n := len(str)
for i := 0; i < n; i++ {
ch := str[i] // 依据下标取字符串中的字符,类型为byte
fmt.Println(i, ch)
}//该方法获取的字符数量是13个
str := "Hello,世界"
for i, ch := range str {
fmt.Println(i, ch)//ch的类型为rune
}//该方法获取的字符数量是9个
在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符。
数组:
//数组定义
[32]byte // 长度为32的数组,每个元素为一个字节
[2*N] struct { x, y int32 } // 复杂类型数组
[1000]*float64 // 指针数组
[3][5]int // 二维数组
[2][2][2]float64 // 等同于[2]([2]([2]float64))
//数组的两种遍历方法
for i := 0; i < len(array); i++ {
fmt.Println("Element", i, "of array is", array[i])
}
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}
数组是一个值类型(value type),所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。
数组切片的创建方法主要有两种——基于数组和直接创建。
基于数组的创建:
package main
import "fmt"
func main() {
// 先定义一个数组
var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 基于数组创建一个数组切片
var mySlice []int = myArray[:5]
fmt.Println("Elements of myArray: ")
for _, v := range myArray {
fmt.Print(v, " ")
}
fmt.Println("\nElements of mySlice: ")
for _, v := range mySlice {
fmt.Print(v, " ")
}
fmt.Println()
}
直接创建:
mySlice1 := make([]int, 5) //创建一个初始元素个数为5的数组切片,元素初始值为0
mySlice2 := make([]int, 5, 10)//创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间
mySlice3 := []int{1, 2, 3, 4, 5}//直接创建并初始化包含5个元素的数组切片
事实上还会有一个匿名数组被创建出来了。
数组切片可以动态增减元素。
数组切片支持Go语言内置的cap()函数和len()函数,append()函数,copy()函数
map是一堆键值对的未排序集合。
var myMap map[string] PersonInfo //map声明
myMap = make(map[string] PersonInfo) //map创建
myMap = make(map[string] PersonInfo, 100) //map创建时指定初始存储能力
//创建并初始化
myMap = map[string] PersonInfo{
“1234”: PersonInfo{“1”, “Jack”, “Room 101,…”},
}
myMap[“1234”] = PersonInfo{“1”, “Jack”, “Room 101,…”} //元素赋值
delete(myMap, “1234”) //元素删除.如果“1234”这个键不存在,那么这个调用将什么都不发生,也不会有什么副作用
value, ok := myMap[“1234”]//元素查找
if ok { // 找到了
// 处理找到的value
}
关于条件语句,需要注意以下几点:
条件语句不需要使用括号将条件包含起来();
无论语句体内有几条语句,花括号{}都是必须存在的;
左花括号{必须与if或者else处于同一行;
在if之后,条件语句之前,可以添加变量初始化语句,使用;间隔;
在有返回值的函数中,不允许将“最终的”return语句包含在if…else…结构中,
否则会编译失败:
switch i {
case 0:
fmt.Printf("0")
case 1:
fmt.Printf("1")
case 2:
fallthrough//执行接下来的条件下的内容(这个例子中执行case 3 中的内容)
case 3:
fmt.Printf("3")
case 4, 5, 6:
fmt.Printf("4, 5, 6")
default:
fmt.Printf("Default")
}
switch {
case 0 <= Num && Num <= 3:
fmt.Printf("0-3")
case 4 <= Num && Num <= 6:
fmt.Printf("4-6")
case 7 <= Num && Num <= 9:
fmt.Printf("7-9")
}
Go语言中的循环语句只支持for关键字
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
sum := 0
for {
sum++
if sum > 100 {
break
}
}
a := []int{1, 2, 3, 4, 5, 6}
for i, j := 0, len(a) – 1; i < j; i, j = i + 1, j – 1 {
a[i], a[j] = a[j], a[i]
}
函数支持多返回值,不定参数(语法糖),闭包
Go语言引入了一个关于错误处理的标准模式,即error接口
type error interface {
Error() string
}
调用时的代码建议按如下方式处理错误情况:
n, err := Foo(0)
if err != nil {
// 错误处理
} else {
// 使用返回值n
}
defer可以定义语句或者函数,会在所属函数退出的时候执行。
一个函数中可以存在多个defer语句,因此需要注意的是,defer语句的调用是遵照先进后出的原则,即最后一个defer语句将最先被执行
panic()类似于java中的throw
recover类似于java中的try catch
panic(404)
panic("network broken")
panic(Error("file not exists"))
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime error caught: %v", r)
}
}()