Go语言

没有”对象”,没有继承多态,没有泛型,没有try/catch

有接口,函数式编程,CSP并发模型(goroutine+channel)

基础语法

变量定义

Go语言没有全局变量的说法,定义在包内(函数外面)的变量叫包内变量

不赋初值,默认int是0,string是空串

不写type,Go也可以自动推断类型

简写,函数内可以把var用 := 代替,用来定义变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import "fmt"

// Go语言没有全局变量的说法,定义在包内(函数外面)的变量叫包内变量
var aa = 3
var ss = "kkk"
var bb = true // bb := true 函数外面定义变量不能用 :=

// 也可以这么定义
var (
aaa = 33
sss = "kkkk"
bbb = true
)
// 不赋初值,默认int是0,string是空串
func variableZeroValue() {
var a int
var s string
fmt.Println(a, s)
}

// 给变量赋初值
func variableInitialValue() {
var a, b int = 3, 4
var s string = "abc"
fmt.Println(a, b, s)
}

// 不写type,Go也可以自动推断类型
func variableTypeDeduction() {
var a, b, c, s = 3, 4, true, "def"
fmt.Println(a, b, c, s)
}

// 函数内可以把var用 := 代替,用来定义变量
func variableShorter() {
a, b, c, s := 3, 4, true, "def"
b = 5
fmt.Println(a, b, c, s)
}

func main() {
fmt.Println("Hello World")
variableZeroValue()
variableInitialValue()
variableTypeDeduction()
variableShorter()
fmt.Println(aa, ss, bb)
}

内建变量类型

布尔类型8位,string类型string是8位字节的集合

bool,string

加u就是无符号整数,不加就是有符号整数;一种是规定长度的,一种是不规定长度的,不规定长度的int,其长度根据操作系统来,操作系统是多少位,他的长度就是多少位

(u)int,(u)int8,(u)int16,(u)int32,(u)int64

uintptr是指针,指针的长度也是根据操作系统来的

uintptr

byte是8位的;rune是字符型,长度是32位的,也就是其他语言的char

byte,rune

浮点数类型,复数类型

float32,float64,complex64,complex128

验证欧拉公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"math"
"math/cmplx"
)

// 验证欧拉公式
func euler() {
fmt.Println(cmplx.Pow(math.E, 1i * math.Pi) + 1)
fmt.Println(cmplx.Exp(1i * math.Pi) + 1)
}

func main() {
c := 3 + 4i
// 复数的库cmplx,取绝对值
fmt.Println(cmplx.Abs(c))
euler()
}

强制类型转换

go语言类型转换是强制的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"math"
)

func triangle() {
var a, b int = 3, 4
var c int
c = int(math.Sqrt(float64(a * a + b * b)))
fmt.Println(c)
}

func main() {
triangle()
}

常量

常量一定要赋值

用const定义常量,而且go语言常量命名不用大写const filename string

const数值可作为各种类型使用

const a, b = 3, 4,又可以是float又可以是int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"math"
)

func consts() {
const filename string = "abc.txt"
// 常量可以不定义类型,a,b的类型就是不确定的,又可以是float又可以是int
const a, b = 3, 4
var c int
c = int(math.Sqrt(a * a + b * b))
fmt.Println(filename, c)
}

func main() {
consts()
}

iota自增

一组const的定义,可以用iota表示自增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func enums() {
const(
cpp = iota
java
python
golang
)

fmt.Println(cpp, java, python, golang)
}

func main() {
enums()
}

iota还可以作为自增值的种子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func enums() {
const(
b = 1 << (10 * iota)
kb
mb
gb
tb
)
fmt.Println(b, kb, mb, gb, tb)
}

func main() {
enums()
}

枚举

go没有枚举类型,用常量表示枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func enums() {
const(
cpp = 0
java = 1
python = 2
golang = 3
)

fmt.Println(cpp, java, python, golang)
}

func main() {
enums()
}

条件语句

if/else

第一种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
"io/ioutil"
)

func main() {
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
}

第二种方式:

if的条件里可以赋值

if的条件里赋值的变量作用域就在这个if语句里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"io/ioutil"
)

func main() {
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
}

switch

switch会自动break,除非使用fallthrough

switch后可以没有表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import "fmt"

func grade(score int) string {
g := ""
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf(
"Wrong score: %d", score))
case score < 60:
g = "F"
case score < 80:
g = "C"
case score < 90:
g = "B"
case score <= 100:
g = "A"
}
return g
}

func main() {
fmt.Println(
grade(0),
grade(59),
grade(60),
grade(82),
grade(99),
grade(100),
grade(101),
)
}

for

省略起始条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"strconv"
)

func convertToBin(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}

func main() {
fmt.Println(
convertToBin(5),
convertToBin(13),
)
}

省略递增条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"bufio"
"fmt"
"os"
)

func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}

scanner := bufio.NewScanner(file)

for scanner.Scan() {
fmt.Println(scanner.Text())
}
}

func main() {
printFile("abc.txt")
}

省略终止条件

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func forever() {
for {
fmt.Println("abc")
}
}

func main() {
forever()
}

函数

多个返回值

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func div(a, b int) (q, r int) {
return a / b, a % b
}

func main() {
fmt.Println(div(13, 3))
}

多个返回值通常用于返回error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

func eval(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
// 不想用到的值可以用_代替
q, _ := div(a, b)
return q, nil
default:
return 0, fmt.Errorf("unsupported operation: %s", op)
}
}

func div(a, b int) (q, r int) {
return a / b, a % b
}

func main() {
fmt.Println(eval(3, 4, "x"))
fmt.Println(div(13, 3))
}

函数的参数也可以是一个函数

匿名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"math"
"reflect"
"runtime"
)

func apply(op func(int, int) int, a, b int) int {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Printf("Calling function %s with args (%d, %d)\n", opName, a, b)
return op(a, b)
}

func main() {
fmt.Println(apply(func(a int, b int) int {
return int(math.Pow(
float64(a), float64(b)))
}, 3, 4))
}

可变参数列表

没有默认参数,可选参数,函数重载,操作符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func sum(numbers ...int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}

func main() {
fmt.Println(sum(1, 2, 3, 4, 5))
}

指针

指针不能运算

go语言只有值传递一种方式

image-20220918113008295 image-20220918113026786 image-20220918114102139
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func swap(a, b *int) {
*b, *a = *a, *b
}

func main() {
a, b := 3, 4
swap(&a, &b)
fmt.Println(a, b)
}

内建容器

数组

数组的遍历—range关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
var arr1 [5]int
arr2 := [3]int{1, 3, 5}
arr3 := [...]int{2, 4, 6, 8, 10}
var grid [4][5]int

fmt.Println(arr1, arr2, arr3)
fmt.Println(grid)

for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i])
}

for i, v := range arr3 {
fmt.Println(i, v)
}
}

数组传递是值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func printArray(arr [5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}

func main() {
var arr1 [5]int
arr3 := [...]int{2, 4, 6, 8, 10}

printArray(arr1)
printArray(arr3)
fmt.Println(arr1, arr3)
}

切片

切片本身没有数据,而是数组的一个视图(view)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func updateSlice(s []int) {
s[0] = 100
}

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:]
fmt.Println(s1) // [2 3 4 5 6 7]
s2 := arr[:]
fmt.Println(s2) // [0 1 2 3 4 5 6 7]

updateSlice(s1)
fmt.Println(s1) // [100 3 4 5 6 7]
fmt.Println(s2) // [0 1 100 3 4 5 6 7]
}

slice的拓展

image-20220918143132904
1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]
fmt.Println(s1) // [2 3 4 5]
s2 := s1[3:5]
fmt.Println(s2) // [5 6]
}

Slice的实现

只要不超过capacity即可

slice可以向后扩展,不可以向前扩展

s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)

image-20220918143235819
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]
fmt.Println(s1) // [2 3 4 5]
s2 := s1[3:5]
fmt.Println(s2) // [5 6]
// s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1))
// s2=[5 6], len(s2)=2, cap(s2)=3
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2))
}

向Slice添加元素

s4和s5不在是对arr的view

添加元素时如果超越cap,系统会重新分配更大的底层数组

s = append(s, val)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]
fmt.Println(s1) // [2 3 4 5]
s2 := s1[3:5]
fmt.Println(s2) // [5 6]

s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
// s3, s4, s5 = [5 6 10] [5 6 10 11] [5 6 10 11 12]
fmt.Println("s3, s4, s5 =", s3, s4, s5)
// [0 1 2 3 4 5 6 10]
fmt.Println(arr)
}

Slice的定义

cap每次空间不够用的时候会翻倍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func printSlice(s []int) {
fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}

func main() {
s := []int{2, 4, 6}
for i := 0; i < 100; i++ {
printSlice(s)
s = append(s, 2 * i + 1)
}
fmt.Println(s)
}

slice可以直接用make定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func printSlice(s []int) {
fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}

func main() {
s2 := make([]int, 16) // len=16, cap=16
s3 := make([]int, 10, 32) // len=10, cap=32
printSlice(s2)
printSlice(s3)
}

Slice的复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func printSlice(s []int) {
fmt.Println(s)
}

func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{2, 4, 6, 8}
copy(s1, s2)
printSlice(s1) // [2 4 6 8 5]
}

Slice删除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func printSlice(s []int) {
fmt.Println(s)
}

func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{2, 4, 6, 8}
copy(s1, s2)
printSlice(s1) // [2 4 6 8 5]
s1 = append(s1[:3], s1[4:]...)
printSlice(s1) // [2 4 6 5]
}

Map

map是无序的,类似hashmap

创建:make(map[string]int)

获取元素:m[key]

key不存在时,获得Value类型的初始值

用value,ok := m[key]来判断是否存在key用

delete删除一个key

使用range遍历key,或者遍历key,value对

不保证遍历顺序,如需顺序,需手动对key排序

使用len获得元素个数

map的key的类型:

map使用哈希表,必须可以比较相等

除了slice,map,function的内建类型都可以作为key

Struct类型不包含上述字段,也可作为key

定义

map[K]V

map[K1]map[K2]v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {
m := map[string]string {
"name" : "ccmouse",
"course" : "golang",
"site" : "imooc",
"quality" : "notbad",
}

fmt.Println(m) // map[course:golang name:ccmouse quality:notbad site:imooc]

m2 := make(map[string]int) // m2 == empty

var m3 map[string]int // m3 == nil

fmt.Println(m2)

fmt.Println(m3)
}

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {
m := map[string]string {
"name" : "ccmouse",
"course" : "golang",
"site" : "imooc",
"quality" : "notbad",
}

fmt.Println(m)
for k, v := range m {
fmt.Println(k, v)
}

for k := range m {
fmt.Println(k)
}

for _, v := range m {
fmt.Println(v)
}
}

支持[ ]操作

key不存在,也不会报错,而是输出zero value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
m := map[string]string {
"name" : "ccmouse",
"course" : "golang",
"site" : "imooc",
"quality" : "notbad",
}

fmt.Println(m["site"]) // imooc
fmt.Println(m["si"]) // 不会报错,打印空值
}

key是否在map中,k1, ok := map[key],如果存在,k1就是值,ok为true;不存在k1为zero value,ok为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
m := map[string]string {
"name" : "ccmouse",
"course" : "golang",
"site" : "imooc",
"quality" : "notbad",
}

k1, ok := m["site"]
fmt.Println(k1, ok) // imooc true
k2, ok := m["si"]
fmt.Println(k2, ok) // false
}

删除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
m := map[string]string {
"name" : "ccmouse",
"course" : "golang",
"site" : "imooc",
"quality" : "notbad",
}
_, ok := m["name"]
fmt.Println(ok) // true
delete(m, "name")
_, ok = m["name"]
fmt.Println(ok) // false
}

字符串和字符串处理

rune相当于go的char

%x表示按16进制输出

使用range遍历pos,rune对

使用utf8.RuneCountInString获得字符数量

使用len获得字节长度

使用[]byte获得字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"fmt"
"unicode/utf8"
)

func main() {
s := "Yes咕咕咕" // UTF-8,一个中文是3个字节
fmt.Println(len(s)) // 12
for _, b := range []byte(s) {
fmt.Printf("%X ", b) // 59 65 73 E5 92 95 E5 92 95 E5 92 95
}
fmt.Println()

for i, ch := range s { // ch is a rune
fmt.Printf("(%d %X) ", i, ch) // (0 59) (1 65) (2 73) (3 5495) (6 5495) (9 5495)
}
fmt.Println()

// 按习惯上的字符个数来计算
fmt.Println("Rune count: ", utf8.RuneCountInString(s)) // Rune count: 6

bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes) // 读取第一个字符和字符的长度
bytes = bytes[size:]
fmt.Printf("%c ", ch)
}
fmt.Println()
for i, ch := range []rune(s) {
fmt.Printf("(%d %c) ", i, ch)
}
fmt.Println()
}

面向对象

go语言仅支持封装,不支持继承和多态

go语言没有class,只有struct

结构的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

func main() {
var root treeNode
fmt.Println(root) // {0 <nil> <nil>}

root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)

nodes := []treeNode {
{value: 3},
{},
{6, nil, &root},
}
fmt.Println(nodes)
}

工厂函数

使用自定义工厂函数

go语言允许返回局部变量的地址,这在C++中是不允许的

C++中局部变量是分配在栈上的,函数一旦退出,局部变量就会被销毁;如果要传出函数体,就要分配在栈上

Java中,new出来的变量一般是放在堆上的,这样才能被GC回收

Go不需要知道变量是分配在堆上还是栈上,垃圾回收机制会自动判断之后是否还会用到这个变量,自动选择在堆上还是栈上分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

// 工厂函数
func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
var root treeNode
fmt.Println(root) // {0 <nil> <nil>}

root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
}
image-20220918204316175

给结构体定义方法

结构体的方法定义在结构体外部

值作为方法的接收者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

// treeNode结构体的方法
func (node treeNode) print() {
fmt.Print(node.value)
}

// 工厂函数
func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
var root treeNode
fmt.Println(root) // {0 <nil> <nil>}

root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)

root.print()
fmt.Println()
}

指针作为方法的接收者

只有使用指针才可以改变结构内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

// treeNode结构体的方法
func (node treeNode) print() {
fmt.Print(node.value)
}

func (node *treeNode) setValue(value int) {
node.value = value
}

// 工厂函数
func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)

root.right.left.setValue(4)
root.right.left.print() // 4
fmt.Println()
}

nil指针也可以调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

// treeNode结构体的方法
func (node treeNode) print() {
fmt.Print(node.value)
}

func (node *treeNode) setValue(value int) {
if node == nil {
fmt.Println("Setting value to nil node. " +
"Ignored")
return
}
node.value = value
}

// 工厂函数
func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)

root.right.left.setValue(4)
root.right.left.print()
fmt.Println()

var pRoot *treeNode
pRoot.setValue(200)
pRoot = &root
pRoot.setValue(300)
pRoot.print()
}

树的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

type treeNode struct {
value int
left, right *treeNode
}

// treeNode结构体的方法
func (node treeNode) print() {
fmt.Print(node.value, " ")
}

func (node *treeNode) setValue(value int) {
if node == nil {
fmt.Println("Setting value to nil node. " +
"Ignored")
return
}
node.value = value
}

func (node *treeNode) traverse() {
if node == nil {
return
}
node.left.traverse()
node.print()
node.right.traverse()
}

// 工厂函数
func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
root.right.left.setValue(4)

root.traverse()
}

值接收者VS指针接收者

要改变内容必须使用指针接收者

结构过大也考虑使用指针接收者

一致性:如有指针接收者,最好都是指针接收者

值接收者是go语言特有

值/指针接收者均可接收值/指针

包和封装

名字一般使用CamelCase

首字母大写:public

首字母小写:private

每个目录一个包

main包包含可执行入口

为结构定义的方法必须放在同一个包内,可以是不同文件

扩展已有类型

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

1.定义别名

2.使用组合

通过组合

通过组合实现后序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
"golang/tree"
)

// 实现后序遍历
type myTreeNode struct {
node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}

left := myTreeNode{myNode.node.Left}
left.postOrder()
right := myTreeNode{myNode.node.Right}
right.postOrder()
myNode.node.Print()
}

func main() {
var root tree.Node
root = tree.Node{Value: 3}
root.Left = &tree.Node{}
root.Right = &tree.Node{5, nil, nil}
root.Right.Left = new(tree.Node)
root.Left.Right = tree.CreateNode(2)
root.Right.Left.SetValue(4)

root.Traverse()
fmt.Println()
myRoot := myTreeNode{&root}
myRoot.postOrder()
fmt.Println()
}

定义别名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package queue

type Queue []int

func (q *Queue) Push(v int) {
*q = append(*q, v)
}

func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}

func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"golang/queue"
)

func main() {
q := queue.Queue{1}

q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
}

GOPATH环境变量

默认在~/go(uniX,linux),%USERPROFILE%\go(windows)

官方推荐:所有项目和第三方库都放在同一个GOPATH下

也可以将每个项目放在不同的GOPATH

go get获取第三方库

go get命令演示

使用gopm来获取无法下载的包

go get -v github.com/gpmgo/gopm

面向接口

duck typing

像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子

描述事物的外部行为而非内部结构

image-20221008223004982
-------------本文结束感谢您的阅读-------------