Skip to content

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
  1. 不支持隐式类型转换。
  2. 不支持指针运算
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

  1. 不限制常量或者整数
  2. 不需要 case break
  3. 支持表达式
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 与工厂模式

  1. map 的 value 可以是一个方法
  2. 与 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

  1. 元素唯一性
  2. 基本操作 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

和其他编程语言的差异

  1. string 是数据类型,不是引用或者指针类型
  2. string 是只读的 byte slice, len 函数返回其包含的 byte 数
  3. 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 中,函数是一等公民。

与其他语言的相比:

  1. 支持多个返回值
  2. 所有参数都是值传递: slice map channel 会有传引用的错觉
  3. 函数可以作为变量的值
  4. 函数可以作为参数和返回值

多返回值

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  
}