极客时间 《Go语言从入门到实战》学习笔记。

开发环境

环境配置略。

go1.8以后不需要设置环境变量。

数据类型

声明

//写法1: 通常用于声明变量
//var a int = 1
//var b int = 2
// 写法2: 通常用于声明变量
//var (
//	a int = 1
//	b     = 2
//)
//写法3
a := 1
b := 2

由于go中一个赋值语句可以对多个变量进行赋值,因此交换变量的值可写作:

func TestExchange(t *testing.T) {
	a := 1
	b := 2
	// 可以在一个赋值语句中对多个语句进行赋值
	a,b = b,a
	t.Log(b,a)
}

常量

可以使用iota设置连续的值

// 递增加一
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. 不支持指针运算
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中: 相同维数,并且个数相同的才可以比较 每个元素相同的数组才相等

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

条件和循环

for

举例:

1). while条件循环 条件 n < 5

n := 0
for n < 5 {
   n++
   t.Log(n)
}
t.Log("----")

2). 无限循环

n = 0
for {
  n++
  if n > 5 {
    break
  }
  t.Log(n)
}

if

if 支持变量赋值

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. 支持表达式
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")
	}
}

数组和切片

数组

数组声明, 并赋值

var a [3]int
a[0] = 1 // 直接通过下标访问或修改
t.Log(a) // [1 0 0]

数组声明的同时进行赋值

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

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)
}

数组的截取操作

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]

切片

切片的数据结构: 指针 、长度、 容量

	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声明

//初始化并带初始值
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

m2 := make(map[string]int, 10 /*inital capcity*/)
t.Log(m2)
//赋值
m2["11"] = 16

检验空值

不存在的key返回零值,不会报异常。

// 初始化空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

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接口方式一起,可以方便的实现单一方法对象的工厂模式
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.元素个数
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数组可以存放任何数据

初始化

初始化默认值为””

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的存储实现(转换为字节序列的规则)
// 中  => 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 4e2d
t.Logf("中 utf8 %x", s)       // 中 utf8 e4b8ad

遍历字符串的每个rune

sx := "中国"
for _, c := range sx {
   //log中 [1]表示与第一个参数对应
   t.Logf("%[1]c %[1]d %[1]x", c)
   // 中 20013 4e2d
   // 国 22269 56fd
}

字符串的一些方法

// 字符串分割
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. 函数可以作为参数和返回值

多返回值

func returnMultiValues() (int, int) {
	return rand.Intn(10), rand.Intn(20)
}

函数是一等公民

例如,计算方法的执行时间

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 Sum(opts ...int) int {
	ret := 0
	for _, op := range opts {
		ret += op
	}
	return ret
}
//调用
t.Log(Sum(1, 2, 3))    // 6

defer

func TestDefer(t *testing.T) {
	defer func() {
		t.Log("Clean resources")
	}()
	t.Log("Started")
	panic("Fatal error") //异常仍会执行defer
}

将Python Tornado注册到Eureka Go语言学习笔记(二)