✨你好啊,我是“ 罗师傅”,是一名程序猿哦。
🌍主页链接:楚门的世界 - 一个热爱学习和运动的程序猿
☀️博文主更方向为:一起来学go(狗)语言。随着专业的深入会越来越广哦…一起期待。
❤️一个“不想让我曾没有做好的也成为你的遗憾”的博主。
💪很高兴与你相遇,一起加油!

前言

江湖儿女们,听我一言!自我初识Java以来,侠者心中燃起一抹热忱,追求编程的精髓。然而,多年的江湖岁月让我领悟到一种新的力量——Go语言。

🌟🌟🌟基础语法

👻👻变量和常量

🍱变量定义

使用Var关键字

  • var a, b, c bool
  • var s1, s2 string = “hello”, “world”
  • 可以放在函数内,或直接放在包内
  • 使用var()集中定义变量

让编译器自动决定类型

  • var a, b, i, s1, s2 = true, flase, 3, “hello”, “world”

使用:=定义变量

  • a, b, i, s1, s2 := true, flase, 3, “hello”, “world”
  • 只能在函数内使用

🍘内建变量类型

  • bool, string
  • (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uintptr
  • byte, rune
  • float32, float64, complex64, complex128

🍙强制类型转换

🍛常量定义

  • const filename = “abc.txt”
  • const数值可作为各种类型使用
  • const a, b = 3, 4

一次声明多个常量

1
2
3
4
5
6
const (
name = "lwsj"
age = 21
flag = true
price = 12.23
)

🥡使用常量定义枚举类型

  • 普通枚举类型
1
2
3
4
5
6
const (
cpp = 0
java = 1
python = 2
golang = 3
)
  • 自增枚举类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const (
cpp = iota // 自动递增
java
python
golang
)
// b, kb, mb, gb, tb, pb
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
// 1 1024 1048576 1073741824 1099511627776 1125899906842624

🏌变量定义要点回顾

  • 变量类型写在变量名之后
  • 编译器可推测变量类型
  • 咩有char,只有rune
  • 原生支持复数类型

💦💦条件语句

🦸‍♂if

1
2
3
4
5
if contents, err := ioutil.ReadFile(filename); err == nil {
fmt.Println(string(contents))
} else {
fmt.Println("cannot print file contents:", err)
}
  • if的条件里可以赋值
  • if的条件里赋值的变量作用域就在这个if语句里

🤔switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func eval(a, b int, op string) int {
var result int
switch op {
case "+":
result = a + b
case "-":
result = a - b
case "*":
result = a * b
case "/":
result = a / b
default:
panic("unsupported operation: " + op)
}
return result
}
  • switch后可以没有表达式
  • switch会自动break,除非使用fallthrough

🫡🫡循环

🐋for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}

for { // 死循环
fmt.Println("abc")
}

// 转二进制
for ; n > 0; n /= 2 { // 省略初始条件,相当于while
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
  • for的条件里不需要括号
  • for的条件里可以省略初始条件,结束条件,递增表达式
  • 省略初始条件,相当于while

🐙基本语法要点回顾

  • for, if 后面的条件没有括号
  • if条件里也可以定义变量
  • 咩有while
  • switch不需要break,也可以直接switch多个条件

🍭函数

1
2
3
>func div(a, b int) (q, r int) {
return a / b, a % b
>}
  • 返回值类型写在最后面
  • 函数可以返回多个值
  • 函数返回多个值时可以起名字(建议仅用于非常简单的函数)
  • 没有默认参数,可选参数(只有可变参数 (int…))
  • 函数可以作为参数使用

🧋指针

1
2
3
4
var a int = 2
var pa *int = &a
*pa = 3
fmt.Println(a)
  • Go的指针不能运算
  • Go语言只有值传递一种方式

🎊🎊🎊内建容器

🌳🌳数组

1
2
3
4
5
6
7
8
9
10
11
// 数组的定义
var arr1 [5]int
arr2 := [3]int{1, 2, 3}
arr3 := [...]int{2, 4, 6, 8, 10}
var grid [4][5]int
// 数组的遍历
sum := 0
//for i, v := range numbers {
for _, v := range numbers {
sum += v
}
  • 可以通过 _ 省略变量(当我不想使用该变量的时候)
  • 不仅range,任何地方都可以通过 _ 省略变量

🌵为什么要使用range呢?

  • 意义明确,美观
  • c++: 没有类似能力
  • Java/Python:只能for each value, 不能同时获取 i, v

🌲数组是值类型

  • [10]int和[20]int是不同类型
  • 调用func f(arr [10]int) 会拷贝数组
  • 在go语言中一般不直接使用数组(而是用切片

🍀🍀Slice(切片)

1
2
3
4
5
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s := arr[2:6] // s的值为[2,3,4,5] 左闭右开
s[0] = 10 // arr的值变为[0,1,10,3,4,5,6,7]
s1 := arr[2:6] // [2,3,4,5]
s2 := s1[3:5] // [5,6]
  • slice本是没有数据,是对底层array的一个view
  • slice 可以向后扩展,不可以想前扩展
  • s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)

🌍向Slice添加元素

  • 添加元素时如果超越cap,系统会重新分配更大的底层数组
  • 由于是值传递的关系,必须接收append的返回值
  • s = append(s, val)

🌞🌞Map

1
2
3
4
5
6
m := map[string]string{
"name": "ccmouse",
"course": "golang",
"site": "imooc"
"quality": "notbad",
}
  • map[K]V, map[K1]map[K2]V

Map的操作

  • 创建:make(map[string]int)
  • 获取元素:m[key]
  • key不存在时,获得Value类型的初始值
  • 用value, ok:=m[key]来判断是否存在key
  • 用delete删除一个key

Map的遍历

  • 使用range遍历key,或者遍历key, value对
  • 不保证遍历顺序,如需顺序,需手动对key排序

Map的key可以是哪些?

  • map使用哈希表,必须可是比较相等
  • 除了slice,map,function的内建类型都可以作为key
  • Struct类型不包含上述字段,也可作为key

🍠🍠rune

1
2
3
4
5
6
7
8
9
10
11
12
13
// rune相当于是go的char
for i, ch := range s { // ch is a rune = int32
fmt.Printf("(%d %X) ", i, ch)
}
// 获得字符数量
fmt.Println(utf8.RuneCountInString(s))

bytes := []byte(s) // []byte获得字节
for len(bytes) > 0 { // len获得字节长度
ch, size := utf8.DecodeRune(bytes) // ch is a rune = int32
bytes = bytes[size:]
fmt.Printf("%c ", ch)
}
  • 使用range遍历pos,rune对
  • 使用utf8.RuneCountInString获得字符数量
  • 使用len获得字节长度
  • 使用[]byte获得字节

🔥🔥🔥面向对象

  • go语言仅支持封装,不支持继承和多态,更多的组合的形式
  • go语言没有class,只有struct

❤️❤️结构体

1
2
3
4
type TreeNode struct {
value int
left, right *Node
}

💕结构的创建

1
2
3
4
func createNode(value int) *TreeNode {
return &TreeNode{value: value}
}
root.left.right = createNode(2)
  • 使用自定义工厂函数(相当于是构造函数)
  • 注意返回了局部变量的地址

💜为结构定义方法

1
2
3
func (node TreeNode) print() {
fmt.Println(node.value)
}
  • 显示定义和命名方法接收者(表示只有TreeNode类型的变量才能使用print方法)

🍅使用指针作为方法接收者

1
2
3
4
5
6
7
func (node *TreeNode) setValue(value int) {
if node == nil {
fmt.Println("Setting Value to nil node. Ignored.")
return
}
node.value = value
}
  • 只有使用指针才可以改变结构内容
  • nil指针也可以调用方法(所以要判断node == nil)

✌值接收者 vs 指针接收者

  • 要改变内容必须使用指针接收者
  • 结构过大也考虑使用指针接收者
  • 一致性:如有指针接收者,最好都是指针接收者
  • 值接收者是go语言特有的
  • 值接收者可以接收“值/指针”,但是指针接收者只能接受指针

💪💪封装

  • 名字一般使用CamelCase(驼峰命名)
  • 首字母大写:public
  • 首字母小写:private

📢包

  • 每个目录一个包
  • main包包含可执行入口
  • 为结构定义的方法必须放在同一个包内
  • 可以是不同文件

如何扩充系统类型或者别人的类型

  • 定义别名: 最简单
  • 使用组合:最常用
  • 使用内嵌:需要省下许多代码

📣📣📣go语言的依赖管理

  • 依赖的概念:就是用别人写好的库
  • 依赖管理的三个阶段:GOPATH, GOVENDOR, go mod(好用)

GOVENDOR

  • 每个项目有自己的vendor目录,存放第三方库
  • 大量第三方依赖管理工具:glide,dep,go dep, …

go mod

  • 由go命令统一的管理,用户不必关心目录结构
  • 初始化:go mod init
  • 增加依赖:go get 或者 go get -u
  • 更新依赖:go get[@v…], go mod tidy
  • 将旧项目迁移到go mod: go mod init, go build ./…

每一个package main 都应该有自己的包

❤️❤️❤️忙碌的敲代码也不要忘了浪漫鸭!

🍉🍉🍉第一周golang的基础学习到这里就结束啦,这只是go语言的开始,期待下周的学习,我们下周见 拜拜咯~~💪。