Go语言理论一

Posted 知其黑、受其白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言理论一相关的知识,希望对你有一定的参考价值。

阅读目录


.

理论

Go语言的主要特征

1.自动立即回收。
2.更丰富的内置类型。
3.函数多返回值。
4.错误处理。
5.匿名函数和闭包。
6.类型和接口。
7.并发编程。
8.反射。
9.语言交互性。

UTF-8 和 Unicode 有何区别?

  • Unicode 是「字符集」
  • UTF-8 是「编码规则」

字符集:
为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point);

编码规则:
将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)

Go语言命名

1.Go的函数、变量、常量、自定义类型、包(package)的命名方式遵循以下规则:

1)首字符可以是任意的Unicode字符或者下划线
2)剩余字符可以是Unicode字符、下划线、数字
3)字符长度不限

Go语言内置函数

  • append – 用来追加元素到数组、slice中,返回修改后的数组、slice。
  • close – 主要用来关闭 channel。
  • delete – 从map中删除 key 对应的 value。
  • panic – 停止常规的 goroutine (panic 和 recover:用来做错误处理)
  • recover – 允许程序定义 goroutine 的 panic 动作。
  • real – 返回 complex 的实部 (complex、real imag:用于创建和操作复数)
  • imag – 返回 complex 的虚部。
  • make – 用来分配内存,返回 Type 本身(只能应用于 slice, map, channel)
  • new – 用来分配内存,主要用来分配值类型,比如 int、struct。返回指向Type的指针。
  • cap – capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
  • copy – 用于复制和连接 slice,返回复制的数目。
  • len – 来求长度,比如string、array、slice、map、channel ,返回长度
  • print、println – 底层打印函数,在部署环境中建议使用 fmt 包

内置接口 error

// 只要实现了Error()函数,返回值为String的都实现了err接口
type error interface 
    Error()    String

init 函数

1 init 函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等。

2 每个包可以拥有多个 init 函数。

3 包的每个源文件也可以拥有多个 init 函数。

4 同一个包中多个 init 函数的执行顺序 go 语言没有明确的定义(说明)

5 不同包的 init 函数按照包导入的依赖关系决定该初始化函数的执行顺序。

6 init 函数不能被其他函数调用,而是在main函数执行之前,自动被调用。

init 函数和 main 函数的异同

相同点:

两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。

不同点:

init 可以应用于任意包中,且可以重复定义多个。
main 函数只能用于main包中,且只能定义一个。

两个函数的执行顺序:对同一个go文件的init()调用顺序是从上到下的。

Go 语言内置的运算符

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符

算数运算符

关系运算符

逻辑运算符

位运算符

赋值运算符

基本类型

类型长度(字节)默认值说明
bool1false
byte10uint8
rune40Unicode Code Point, int32
int, uint4或8032 或 64 位
int8, uint810-128 ~ 127, 0 ~ 255,byte是uint8 的别名
int16, uint1620-32768 ~ 32767, 0 ~ 65535
int32, uint3240-21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名
int64, uint6480
float3240.0
float6480.0
complex648
complex12816
uintptr4或8以存储指针的 uint32 或 uint64 整数
array值类型
struct值类型
string""UTF-8 字符串
slicenil引用类型
mapnil引用类型
channelnil引用类型
interfacenil接口
functionnil函数

byte 和 rune 类型

Go 语言的字符有以下两种:

  • uint8类型,或者叫 byte 型,代表了ASCII码的一个字符。

  • rune类型,代表一个 UTF-8字符。

package main

import (
	"fmt"
)

func main() 
	traversalString()


// 遍历字符串
func traversalString() 
	// byte
	s := "wgchen博客"
	for i := 0; i < len(s); i++ 
		fmt.Printf("%v(%c) ", s[i], s[i])
	

	fmt.Println()

	//rune
	for _, r := range s 
		fmt.Printf("%v(%c) ", r, r)
	
	fmt.Println()

E:\\go_test>go run main.go
119(w) 103(g) 99(c) 104(h) 101(e) 110(n) 229(å) 141() 154(š) 229(å) 174(®) 162(¢)
119(w) 103(g) 99(c) 104(h) 101(e) 110(n) 21338(博) 23458(客)

E:\\go_test>

数组 Array

1、数组:是同一种数据类型的固定长度的序列。

2、数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。

3、长度是数组类型的一部分,因此,var a[5] intvar a[10]int 是不同的类型。

4、数组可以通过下标进行访问,下标是从 0 开始,最后一个元素下标是:len-1

for i := 0; i < len(a); i++ 

for index, v := range a 

5、访问越界,如果下标在数组合法范围之外,则触发访问越界,会 panic。

6、数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。

7、支持 "=="、"!=" 操作符,因为内存总是被初始化过的。

8、指针数组 [n]*T,数组指针 *[n]T

数组初始化

一维数组

全局:
var arr0 [5]int = [5]int1, 2, 3
var arr1 = [5]int1, 2, 3, 4, 5
var arr2 = [...]int1, 2, 3, 4, 5, 6
var str = [5]string3: "hello world", 4: "tom"

局部:
a := [3]int1, 2           // 未初始化元素值为 0。
b := [...]int1, 2, 3, 4   // 通过初始化值确定数组长度。
c := [5]int2: 100, 4: 200 // 使用索引号初始化元素。
d := [...]struct 
    name string
    age  uint8

    "user1", 10, // 可省略元素类型。
    "user2", 20, // 别忘了最后一行的逗号。

package main

import (
    "fmt"
)

var arr0 [5]int = [5]int1, 2, 3
var arr1 = [5]int1, 2, 3, 4, 5
var arr2 = [...]int1, 2, 3, 4, 5, 6
var str = [5]string3: "hello world", 4: "tom"

func main() 
    a := [3]int1, 2           // 未初始化元素值为 0。
    b := [...]int1, 2, 3, 4   // 通过初始化值确定数组长度。
    c := [5]int2: 100, 4: 200 // 使用引号初始化元素。
    d := [...]struct 
        name string
        age  uint8
    
        "user1", 10, // 可省略元素类型。
        "user2", 20, // 别忘了最后一行的逗号。
    
    fmt.Println(arr0, arr1, arr2, str)
    fmt.Println(a, b, c, d)

[1 2 3 0 0] [1 2 3 4 5] [1 2 3 4 5 6] [   hello world tom]
[1 2 0] [1 2 3 4] [0 0 100 0 200] [user1 10 user2 20]

多维数组

全局
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int1, 2, 3, 7, 8, 9

局部:
a := [2][3]int1, 2, 3, 4, 5, 6
b := [...][2]int1, 1, 2, 2, 3, 3 
// 第 2 纬度不能用 "..."。
package main

import (
    "fmt"
)

var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int1, 2, 3, 7, 8, 9

func main() 
    a := [2][3]int1, 2, 3, 4, 5, 6
    b := [...][2]int1, 1, 2, 2, 3, 3 
    // 第 2 纬度不能用 "..."。
    
    fmt.Println(arr0, arr1)
    fmt.Println(a, b)

输出结果:

[[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]
[[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]

值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。

package main

import (
    "fmt"
)

func test(x [2]int) 
    fmt.Printf("x: %p\\n", &x)
    x[1] = 1000


func main() 
    a := [2]int
    fmt.Printf("a: %p\\n", &a)

    test(a)
    fmt.Println(a)

输出结果:

a: 0xc42007c010
x: 0xc42007c030
[0 0]

内置函数 len 和 cap 都返回数组长度 (元素数量)。

package main

func main() 
    a := [2]int
    println(len(a), cap(a)) // 2 2

切片 Slice

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

1、切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。

2、切片的长度可以改变,因此,切片是一个可变的数组。

3、切片遍历方式和数组一样,可以用 len() 求长度。表示可用元素数量,读写操作不能超过该限制。

4、cap 可以求出 slice 最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中 array 是 slice 引用的数组。

5、切片的定义:var 变量名 [] 类型,比如 var str []string var arr []int

6、如果 slice == nil,那么 len、cap 结果都等于 0。

创建切片的各种方式

package main

import "fmt"

func main() 
	//1.声明切片
	var s1 []int
	if s1 == nil 
		fmt.Println("是空")
	 else 
		fmt.Println("不是空")
	

	// 2.:=
	s2 := []int
	// 3.make()
	var s3 []int = make([]int, 0)
	fmt.Println(s1, s2, s3)
	// [] [] []

	// 4.初始化赋值
	var s4 []int = make([]int, 0, 0)
	fmt.Println(s4)
	// []

	s5 := []int1, 2, 3
	fmt.Println(s5)
	// [1 2 3]

	// 5.从数组切片
	arr := [5]int1, 2, 3, 4, 5
	var s6 []int // 前包后不包
	s6 = arr[1:4]
	fmt.Println(s6)
	// [2 3 4]

切片初始化

全局:
var arr = [...]int0, 1, 2, 3, 4, 5, 6, 7, 8, 9
var slice0 []int = arr[start:end] 
var slice1 []int = arr[:end]        
var slice2 []int = arr[start:]        
var slice3 []int = arr[:] 
var slice4 = arr[:len(arr)-1]      
//去掉切片的最后一个元素

局部:
arr2 := [...]int9, 8, 7, 6, 5, 4, 3, 2, 1, 0
slice5 := arr[start:end]
slice6 := arr[:end]        
slice7 := arr[start:]     
slice8 := arr[:]  
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
package main

import (
	"fmt"
)

var arr = [...]int0, 1, 2, 3, 4, 5, 6, 7, 8, 9
var slice0 []int = arr[2:8]

// 可以简写为 var slice []int = arr[:end]
var slice1 []int = arr[0:6]

// 可以简写为 var slice[]int = arr[start:]
var slice2 []int = arr[5:10]

// var slice []int = arr[:]
var slice3 []int = arr[0:len(arr)]

// 去掉切片的最后一个元素
var slice4 = arr[:len(arr)-1]

func main() 
	fmt.Printf("全局变量:arr %v\\n", arr)
	fmt.Printf("全局变量:slice0 %v\\n", slice0)
	fmt.Printf("全局变量:slice1 %v\\n", slice1)
	fmt.Printf("全局变量:slice2 %v\\n", slice2)
	fmt.Printf("全局变量:slice3 %v\\n", slice3)
	fmt.Printf("全局变量:slice4 %v\\n", slice4)
	fmt.Printf("-----------------------------------\\n")

	arr2 := [...]int9, 8, 7, 6, 5, 4, 3, 2, 1, 0
	slice5 := arr[2:8]
	slice6 := arr[0:6]         //可以简写为 slice := arr[:end]
	slice7 := arr[5:10]        //可以简写为 slice := arr[start:]
	slice8 := arr[0:len(arr)]  //slice := arr[:]
	slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
	fmt.Printf("局部变量: arr2 %v\\n", arr2)
	fmt.Printf("局部变量: slice5 %v\\n", slice5)
	fmt.Printf("局部变量: slice6 %v\\n", slice6)
	fmt.Printf("局部变量: slice7 %v\\n", slice7)
	fmt.Printf("局部变量: slice8 %v\\n", slice8)
	fmt.Printf("局部变量: slice9 %v\\n", slice9)

E:\\go_test>go run main.go
全局变量:arr [0 1 2 3 4 5 6 7 8 9]
全局变量:slice0 [2 3 4 5 6 7]
全局变量:slice1 [0 1 2 3 4 5]
全局变量:slice2 [5 6 7 8 9]
全局变量:slice3 [0 1 2 3 4 5 6 7 8 9]
全局变量:slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
局部变量: arr2 [9 8 7 6 5 4 3 2 1 0]
局部变量: slice5 [2 3 4 5 6 7]
局部变量: slice6 [0 1 2 3 4 5]
局部变量: slice7 [5 6 7 8 9]
局部变量: slice8 [0 1 2 3 4 5 6 7 8 9]
局部变量: slice9 [0 1 2 3 4 5 6 7 8]

E:\\go_test>

通过 make 来创建切片

var slice []type = make([]type, len)

slice  := make([]type, len)
slice  := make([]type, len, cap)

读写操作实际目标是底层数组,只需注意索引号的差别。

package main

import (
    "fmt"
)

func main() 
    data := [...]int0, 1, 2, 3, 4, 5

    s := data[2:4]
    s[0] += 100
    s[1] += 200

    fmt.Println(s)
    fmt.Println(data)

[102 203]
[0 1 102 203 4 5]

可直接创建 slice 对象,自动分配底层数组。

package main

import "fmt"

func main() 
	// 通过初始化表达式构造,可使用索引号。
    s1 := []int0, 1, 2, 3, 8: 100 
    fmt.Println(s1, len(s1), cap(s1))

	// 使用 make 创建,指定 len 和 cap 值。
    s2 := make([]int, 6, 8) 
    fmt.Println(s2, len(s2), cap(s2))

	// 省略 cap,相当于 cap = len。
    s3 := makeGo语言面向函数和接口编程

带你学够浪:Go语言基础系列 - 10分钟学方法和接口

Go语言之Go 语言方法

Go语言之函数方法

3.16 Go微服务实战(微服务理论) --- Go语言基于ES-CQRS的微服务实践

3.16 Go微服务实战(微服务理论) --- Go语言基于ES-CQRS的微服务实践