Go笔记(一):基本类型、运算符、条件、方法
开发环境
环境配置略。
go1.8 以后不需要设置环境变量。
数据类型
声明
go
//写法1: 通常用于声明变量
//var a int = 1
//var b int = 2
// 写法2: 通常用于声明变量
//var (
// a int = 1
// b = 2
//)
//写法3
a := 1
b := 2
//写法1: 通常用于声明变量
//var a int = 1
//var b int = 2
// 写法2: 通常用于声明变量
//var (
// a int = 1
// b = 2
//)
//写法3
a := 1
b := 2
由于 go 中一个赋值语句可以对多个变量进行赋值,因此交换变量的值可写作:
go
func TestExchange(t *testing.T) {
a := 1
b := 2
// 可以在一个赋值语句中对多个语句进行赋值
a,b = b,a
t.Log(b,a)
}
func TestExchange(t *testing.T) {
a := 1
b := 2
// 可以在一个赋值语句中对多个语句进行赋值
a,b = b,a
t.Log(b,a)
}
常量
可以使用 iota 设置连续的值
go
// 递增加一
const (
Monday = iota + 1
Tuesday
Wednesday
Thursday
)
// 位运算
const (
Open = 1 << iota
Close
Pending
)
func TestXx(test *testing.T) {
test.Log(Monday, Tuesday, Wednesday) //1 2 3
test.Log("-----")
test.Log(Open, Close, Pending) // 1 2 4
test.Log(Pending | Close) // 6
}
// 递增加一
const (
Monday = iota + 1
Tuesday
Wednesday
Thursday
)
// 位运算
const (
Open = 1 << iota
Close
Pending
)
func TestXx(test *testing.T) {
test.Log(Monday, Tuesday, Wednesday) //1 2 3
test.Log("-----")
test.Log(Open, Close, Pending) // 1 2 4
test.Log(Pending | Close) // 6
}
基本类型
- bool
- string
- int unit int64 int32 等
- byte
- rune
- float32 float64
- complex64 complex128
- 不支持隐式类型转换。
- 不支持指针运算
go
type MyInt int
func TestImplicit(t *testing.T) {
var a MyInt = 1
var b int = 111
//t.Log(a+b) 编译错误
t.Log(a + MyInt(b))
}
//2. 不支持指针运算
//// string是值类型,其默认的初始化值为空字符串,而不是nil
func TestPoint(t *testing.T) {
a := 1
aPtr := &a
//aPtr += 1 编译错误,不支持指针运算
t.Log(a, aPtr)
// %T 打印类型
t.Logf("%T %T", a, aPtr)
}
func TestString(t *testing.T) {
var s string
t.Log(s) // "
t.Log(len(s)) // 0
//字符串不会为nil
}
type MyInt int
func TestImplicit(t *testing.T) {
var a MyInt = 1
var b int = 111
//t.Log(a+b) 编译错误
t.Log(a + MyInt(b))
}
//2. 不支持指针运算
//// string是值类型,其默认的初始化值为空字符串,而不是nil
func TestPoint(t *testing.T) {
a := 1
aPtr := &a
//aPtr += 1 编译错误,不支持指针运算
t.Log(a, aPtr)
// %T 打印类型
t.Logf("%T %T", a, aPtr)
}
func TestString(t *testing.T) {
var s string
t.Log(s) // "
t.Log(len(s)) // 0
//字符串不会为nil
}
运算符
算术运算符
- /
% 求余
++ 自增
-- 自减
- /
go 没有前置的 ++ , -- 如 ++a, --b
比较运算符:
== != > < >= <=
比较数组,go 中:
相同维数,并且个数相同的才可以比较
每个元素相同的数组才相等
go
func TestCompareArr(t *testing.T) {
a := [...]int{1, 2, 3, 4}
//b := [...]int{1, 2, 3, 4, 5}
c := [...]int{1, 2, 3, 4}
//t.Log(a == b) 编译错误
t.Log(a == c)
}
func TestCompareArr(t *testing.T) {
a := [...]int{1, 2, 3, 4}
//b := [...]int{1, 2, 3, 4, 5}
c := [...]int{1, 2, 3, 4}
//t.Log(a == b) 编译错误
t.Log(a == c)
}
逻辑运算符
略
位运算符 & | ^ << >>
按位清零, 1 表示将表达式左边对应的位 清 0
1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0
1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0
条件和循环
for
举例:
1). while 条件循环 条件 n < 5
go
n := 0
for n < 5 {
n++
t.Log(n)
}
t.Log("----")
n := 0
for n < 5 {
n++
t.Log(n)
}
t.Log("----")
2). 无限循环
go
n = 0
for {
n++
if n > 5 {
break
}
t.Log(n)
}
n = 0
for {
n++
if n > 5 {
break
}
t.Log(n)
}
if
if 支持变量赋值
go
func TestIf(t *testing.T) {
if a := 1 == 1; a {
t.Log("true")
}
//if v,err := someFun(); err = nil {
//}else {
//}
}
func TestIf(t *testing.T) {
if a := 1 == 1; a {
t.Log("true")
}
//if v,err := someFun(); err = nil {
//}else {
//}
}
switch
- 不限制常量或者整数
- 不需要 case break
- 支持表达式
go
func TestSwitch(t *testing.T) {
// 1. 不限制常量或者整数
// 2. 不需要case break
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "linux":
fmt.Println("Linux")
default:
fmt.Printf("%s", os)
}
// 3. case 表达式
num := 99
switch {
case 0 <= num && num <= 88:
t.Log("A")
default:
t.Log("B")
}
switch num {
case 0, 2:
t.Log("B3")
case 100, 99:
t.Log("B2")
default:
t.Log("B1")
}
}
func TestSwitch(t *testing.T) {
// 1. 不限制常量或者整数
// 2. 不需要case break
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "linux":
fmt.Println("Linux")
default:
fmt.Printf("%s", os)
}
// 3. case 表达式
num := 99
switch {
case 0 <= num && num <= 88:
t.Log("A")
default:
t.Log("B")
}
switch num {
case 0, 2:
t.Log("B3")
case 100, 99:
t.Log("B2")
default:
t.Log("B1")
}
}
数组和切片
数组
数组声明, 并赋值
go
var a [3]int
a[0] = 1 // 直接通过下标访问或修改
t.Log(a) // [1 0 0]
var a [3]int
a[0] = 1 // 直接通过下标访问或修改
t.Log(a) // [1 0 0]
数组声明的同时进行赋值
go
b := [3]int{1, 2, 3}
c := [2][2]int{{1, 2}, {3, 4}}
t.Log(b, c) // [1 2 3] [[1 2] [3 4]]
b := [3]int{1, 2, 3}
c := [2][2]int{{1, 2}, {3, 4}}
t.Log(b, c) // [1 2 3] [[1 2] [3 4]]
数组遍历,直接访问下标或者 foreach
go
arr3 := [...]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(arr3); i++ {
t.Log(arr3[i])
}
t.Log("----")
for index, e := range arr3 {
t.Log(index, e)
}
t.Log("----")
// 使用`_`忽略
for _, e := range arr3 {
t.Log(e)
}
arr3 := [...]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(arr3); i++ {
t.Log(arr3[i])
}
t.Log("----")
for index, e := range arr3 {
t.Log(index, e)
}
t.Log("----")
// 使用`_`忽略
for _, e := range arr3 {
t.Log(e)
}
数组的截取操作
go
a := [...]int{1, 2, 3, 4, 5, 6, 7}
// a[开始索引(包含), 结束索引(不包含)] => [index1,index2)
t.Log(a[1:2]) // 2
t.Log(a[1:len(a)]) //[2 3 4 5 6 7]
t.Log(a[:]) //[1 2 3 4 5 6 7]
a := [...]int{1, 2, 3, 4, 5, 6, 7}
// a[开始索引(包含), 结束索引(不包含)] => [index1,index2)
t.Log(a[1:2]) // 2
t.Log(a[1:len(a)]) //[2 3 4 5 6 7]
t.Log(a[:]) //[1 2 3 4 5 6 7]
切片
切片的数据结构: 指针 、长度、 容量
go
var s0 []int
t.Log(s0, len(s0), cap(s0))
s0 = append(s0, 1) // [] 0 0
t.Log(s0, len(s0), cap(s0)) // [1] 1 1
//可以使用make来初始化
s2 := make([]int, 3, 5) //长度是3 容量是5
t.Log(s2, len(s2), cap(s2)) //[0 0 0] 3 5
// t.Log(s2[4]) 会越界
s2 = append(s2, 8)
t.Log(s2, len(s2), cap(s2)) //[0 0 0 8] 4 5
//切片growth ,长度可变
//两种声明方法均可
//var s1 []int
s1 := []int{}
for i := 0; i < 10; i++ {
s1 = append(s1, i)
t.Log("--", len(s1), cap(s1))
// -- 1 1
// -- 2 2
// -- 3 4
// -- 4 4
// -- 5 8
// -- 6 8
// -- 7 8
// -- 8 8
// -- 9 16
// -- 10 16
//可以看到容量呈倍数x2增长
}
var s0 []int
t.Log(s0, len(s0), cap(s0))
s0 = append(s0, 1) // [] 0 0
t.Log(s0, len(s0), cap(s0)) // [1] 1 1
//可以使用make来初始化
s2 := make([]int, 3, 5) //长度是3 容量是5
t.Log(s2, len(s2), cap(s2)) //[0 0 0] 3 5
// t.Log(s2[4]) 会越界
s2 = append(s2, 8)
t.Log(s2, len(s2), cap(s2)) //[0 0 0 8] 4 5
//切片growth ,长度可变
//两种声明方法均可
//var s1 []int
s1 := []int{}
for i := 0; i < 10; i++ {
s1 = append(s1, i)
t.Log("--", len(s1), cap(s1))
// -- 1 1
// -- 2 2
// -- 3 4
// -- 4 4
// -- 5 8
// -- 6 8
// -- 7 8
// -- 8 8
// -- 9 16
// -- 10 16
//可以看到容量呈倍数x2增长
}
append 返回切片的好处是多个切片可以共享一个结构,节省了资源。
map
map 声明
go
//初始化并带初始值
m := map[string]int{"one": 1, "two": 2}
//初始化空map
m1 := map[string]int{}
t.Log(m) // map[one:1 two:2]
t.Log(m1) // map[]
//初始化并带初始值
m := map[string]int{"one": 1, "two": 2}
//初始化空map
m1 := map[string]int{}
t.Log(m) // map[one:1 two:2]
t.Log(m1) // map[]
初始化 capcity
go
m2 := make(map[string]int, 10 /*inital capcity*/)
t.Log(m2)
//赋值
m2["11"] = 16
m2 := make(map[string]int, 10 /*inital capcity*/)
t.Log(m2)
//赋值
m2["11"] = 16
检验空值
不存在的 key 返回零值,不会报异常。
go
// 初始化空map
m1 := map[int]int{}
// 不存在的key,返回零值. 不会返回nil
t.Log(m1[1]) // 0
m1[3] = 100
// 因此我们取值的时候需要判断是否存在
if v, ok := m1[3]; ok {
t.Log(ok, ". value is ", v) // true . value is 100
} else {
t.Log("key 3 not existing.")
}
// 初始化空map
m1 := map[int]int{}
// 不存在的key,返回零值. 不会返回nil
t.Log(m1[1]) // 0
m1[3] = 100
// 因此我们取值的时候需要判断是否存在
if v, ok := m1[3]; ok {
t.Log(ok, ". value is ", v) // true . value is 100
} else {
t.Log("key 3 not existing.")
}
遍历
range
go
func TestTravelMao(t *testing.T) {
m := map[string]int{"one": 1, "two": 2}
for k, v := range m {
t.Log(k, v)
}
}
func TestTravelMao(t *testing.T) {
m := map[string]int{"one": 1, "two": 2}
for k, v := range m {
t.Log(k, v)
}
}
map 与工厂模式
- map 的 value 可以是一个方法
- 与 Go 的 Dock type 接口方式一起,可以方便的实现单一方法对象的工厂模式
go
m := map[int]func(op int) int{}
m[1] = func(op int) int {
return 10 * op
}
m[2] = func(op int) int {
return op
}
t.Log(m, m[1](1), m[2](2)) // map[1:0x10faf80 2:0x10fafa0] 10 2
m := map[int]func(op int) int{}
m[1] = func(op int) int {
return 10 * op
}
m[2] = func(op int) int {
return op
}
t.Log(m, m[1](1), m[2](2)) // map[1:0x10faf80 2:0x10fafa0] 10 2
实现 Set
没有内置的 Set ,可以用 map[type]bool
- 元素唯一性
- 基本操作 a.添加 b.判断元素是否存在 c.删除元素 d.元素个数
go
mySet := map[int]bool{}
mySet[1] = true
n := 1
if mySet[n] {
t.Logf("%d is exsisting", n)
} else {
t.Logf("%d not exsisting", n)
}
//元素个数 len(mySet)//删除元素
delete(mySet, 1)
mySet := map[int]bool{}
mySet[1] = true
n := 1
if mySet[n] {
t.Logf("%d is exsisting", n)
} else {
t.Logf("%d not exsisting", n)
}
//元素个数 len(mySet)//删除元素
delete(mySet, 1)
string
和其他编程语言的差异
- string 是数据类型,不是引用或者指针类型
- string 是只读的 byte slice, len 函数返回其包含的 byte 数
- string 的 byte 数组可以存放任何数据
初始化
初始化默认值为""
go
var s string
t.Log(s) //默认值为 ""s = "hello world"
t.Log(s, len(s)) //hello world 11
s = "\xE4\xB8\xA5" // '严'字的二进制存储。 长度3个byte
t.Log(s, len(s)) // 严 3```
string 是不可变的 byte slice
`//s[1] = '3'` 编译报错
### Unicode 和 UTF8
1. unicode 是一种字符集(code point)
2. utf8 是 unicode 的存储实现(转换为字节序列的规则)
```go
// 中 => unicode 4e2d => string/[]byte [0xe4,0xb8,0xad]s = "中"
t.Log(len(s)) // 6
c := []rune(s) // rune表示它的unicode
t.Log(c) // [20013 22269]
t.Logf("中 unicode %x", c[0]) // 中 unicode 4e2dt.Logf("中 utf8 %x", s) // 中 utf8 e4b8ad```
### 遍历字符串的每个 rune
```go
sx := "中国"
for _, c := range sx {
//log中 [1]表示与第一个参数对应
t.Logf("%[1]c %[1]d %[1]x", c)
// 中 20013 4e2d// 国 22269 56fd}
var s string
t.Log(s) //默认值为 ""s = "hello world"
t.Log(s, len(s)) //hello world 11
s = "\xE4\xB8\xA5" // '严'字的二进制存储。 长度3个byte
t.Log(s, len(s)) // 严 3```
string 是不可变的 byte slice
`//s[1] = '3'` 编译报错
### Unicode 和 UTF8
1. unicode 是一种字符集(code point)
2. utf8 是 unicode 的存储实现(转换为字节序列的规则)
```go
// 中 => unicode 4e2d => string/[]byte [0xe4,0xb8,0xad]s = "中"
t.Log(len(s)) // 6
c := []rune(s) // rune表示它的unicode
t.Log(c) // [20013 22269]
t.Logf("中 unicode %x", c[0]) // 中 unicode 4e2dt.Logf("中 utf8 %x", s) // 中 utf8 e4b8ad```
### 遍历字符串的每个 rune
```go
sx := "中国"
for _, c := range sx {
//log中 [1]表示与第一个参数对应
t.Logf("%[1]c %[1]d %[1]x", c)
// 中 20013 4e2d// 国 22269 56fd}
字符串的一些方法
go
// 字符串分割
s1 := "A,B,C"
parts := strings.Split(s1, ",") //[A B C]
for i, part := range parts {
t.Log(part, i)
}
//字符串拼接
joinded := strings.Join(parts, "-")
t.Logf(joinded) //A-B-C
//字符串转换
s2 := strconv.Itoa(10) //数字转换成了字符串
t.Log(s2)
//字符串转换为数字
if i, err := strconv.Atoi("10"); err == nil {
t.Log(10 + i)
}
// 字符串分割
s1 := "A,B,C"
parts := strings.Split(s1, ",") //[A B C]
for i, part := range parts {
t.Log(part, i)
}
//字符串拼接
joinded := strings.Join(parts, "-")
t.Logf(joinded) //A-B-C
//字符串转换
s2 := strconv.Itoa(10) //数字转换成了字符串
t.Log(s2)
//字符串转换为数字
if i, err := strconv.Atoi("10"); err == nil {
t.Log(10 + i)
}
function
go 中,函数是一等公民。
与其他语言的相比:
- 支持多个返回值
- 所有参数都是值传递: slice map channel 会有传引用的错觉
- 函数可以作为变量的值
- 函数可以作为参数和返回值
多返回值
go
func returnMultiValues() (int, int) {
return rand.Intn(10), rand.Intn(20)
}
func returnMultiValues() (int, int) {
return rand.Intn(10), rand.Intn(20)
}
函数是一等公民
例如,计算方法的执行时间
go
func timeSpent(inner func(op int) int) func(op int) int {
//入参是函数 返回也是函数
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent:", time.Since(start).Seconds())
return ret
}
}
func slowFunc(op int) int {
time.Sleep(time.Second * 1)
return op
}
// 调用
timeSpent(slowFunc)(1)
func timeSpent(inner func(op int) int) func(op int) int {
//入参是函数 返回也是函数
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent:", time.Since(start).Seconds())
return ret
}
}
func slowFunc(op int) int {
time.Sleep(time.Second * 1)
return op
}
// 调用
timeSpent(slowFunc)(1)
可变参数
go
func Sum(opts ...int) int {
ret := 0
for _, op := range opts {
ret += op
}
return ret
}
//调用
t.Log(Sum(1, 2, 3)) // 6
func Sum(opts ...int) int {
ret := 0
for _, op := range opts {
ret += op
}
return ret
}
//调用
t.Log(Sum(1, 2, 3)) // 6
defer
go
func TestDefer(t *testing.T) {
defer func() {
t.Log("Clean resources")
}()
t.Log("Started")
panic("Fatal error") //异常仍会执行defer
}
func TestDefer(t *testing.T) {
defer func() {
t.Log("Clean resources")
}()
t.Log("Started")
panic("Fatal error") //异常仍会执行defer
}