Go语言基础知识总结(持续中)

时间:2021-08-02
本文章向大家介绍Go语言基础知识总结(持续中),主要包括Go语言基础知识总结(持续中)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Go基础知识总结

变量声明

Go语言中的变量需要声明以后才可以使用(需要提前定义变量)并且声明后必须使用(不适用会报错)

标准声明

var 变量名 变量类型

example:

var name string
var id int
var isOk bool

多变量声明可以整合在一起

var (
	name string
	id int
	isOk bool

)

变量初始化

Go语言在声明变量的时候,会自动对变量对应的内存区进行初始化操作。

var 变量名 变量类型 = 表达式

example:

var name string = "A2rcher"
var id int = 123

类型推断

Go语言提供了一种简易的方式,不需要提供变量类型,可以根据后面的初始化操作后自动推断其变量类型

var name = "A2rcher"
var id = 123

短变量声明

在之后学习到的函数中,还可以用更简单的方式来声明一个变量(也是最常用的声明方法),使用:=声明并初始化变量

package main
import (
	"fmt"
)
var m = 100 //全局变量

func main(){
	n:=100
	m:=300//声明并初始化局部变量
	fmt.Println(m, n)
}

匿名变量

所谓匿名,通俗的讲就是没有名字的变量,用下划线_表示,匿名变量不占用命名空间(Go语言中变量声明后必须使用,匿名变量除外),不会分配内存,所以匿名变量之间不存在重复声明。

package main

import "fmt"

func foo() (string, int) {

	return "A2rcher", 20
}

func main() {
	x, _ := foo()
	_, y := foo()

	fmt.Println("x=", x, "y=", y)

}

常量

变量中还有一中是常量,它相对于变量来讲是永恒不变的值,对于变量来说一开始可以不做赋值直接定义类型,但是对于常量来讲他需要在定义的时候就要赋值,用const

const p = 3.1415
const e = 2.7182

多常量声明可以整合在一起

const (
	p = 3.1415
	e = 2.7182
)

iota常量计算器

iota是Go语言中的常量计算器,在常量表达式中使用。

package main

import "fmt"

const (
	n1 = iota
	n2
	n3
	n4
)

func main() {

	fmt.Println(n1)//0
	fmt.Println(n2)//1
	fmt.Println(n3)//2
	fmt.Println(n4)//3
}

iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用。

使用下划线_可以跳过其中一个

package main

import "fmt"

const (
	n1 = iota//0
	_		 //1
	n2		 //2
	n3		 //3
	n4		 //4
)

func main() {

	fmt.Println(n1)//0
	fmt.Println(n2)//2
	fmt.Println(n3)//3
	fmt.Println(n4)//4
}

数据类型

整型

类型 描述
uint8 无符号 8位整型 (0 到 255)
uint16 无符号 16位整型 (0 到 65535)
uint32 无符号 32位整型 (0 到 4294967295)
uint64 无符号 64位整型 (0 到 18446744073709551615)
int8 有符号 8位整型 (-128 到 127)
int16 有符号 16位整型 (-32768 到 32767)
int32 有符号 32位整型 (-2147483648 到 2147483647)
int64 有符号 64位整型 (-9223372036854775808 到 9223372036854775807)

特殊整型

类型 描述
uint 32位操作系统上就是uint32,64位操作系统上就是uint64
int 32位操作系统上就是int32,64位操作系统上就是int64
uintptr 无符号整型,用于存放一个指针

浮点型

Go语言支持两种浮点型数:float32float64。这两种浮点型数据格式遵循IEEE 754标准: float32 的浮点数的最大范围约为 3.4e38,可以使用常量定义:math.MaxFloat32float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64

打印浮点数时,可以使用fmt包配合动词%f,代码如下:

package main
import (
        "fmt"
        "math"
)
func main() {
        fmt.Printf("%f\n", math.Pi)
        fmt.Printf("%.2f\n", math.Pi)
}

复数

complex64和complex128

var c1 complex64
c1 = 1 + 2i
var c2 complex128
c2 = 2 + 3i
fmt.Println(c1)
fmt.Println(c2)

复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。

布尔

字符串

byte和rune

运算符

运算符 描述
+ 相加
- 相减
* 相乘
/ 相除
% 求余
运算符 描述
== 检查两个值是否相等,如果相等返回 True 否则返回 False。
!= 检查两个值是否不相等,如果不相等返回 True 否则返回 False。
> 检查左边值是否大于右边值,如果是返回 True 否则返回 False。
>= 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
< 检查左边值是否小于右边值,如果是返回 True 否则返回 False。
<= 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。
运算符 描述
&& 逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。
! 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。
运算符 描述
& 参与运算的两数各对应的二进位相与。 (两位均为1才为1)
| 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1)
^ 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1)
<< 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。
>> 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。
运算符 描述
= 简单的赋值运算符,将一个表达式的值赋给一个左值
+= 相加后再赋值
-= 相减后再赋值
*= 相乘后再赋值
/= 相除后再赋值
%= 求余后再赋值
<<= 左移后赋值
>>= 右移后赋值
&= 按位与后赋值
|= 按位或后赋值
^= 按位异或后赋值

流程控制

if else

for 初始语句;条件表达式;结束语句{
	循环语句
}

example:

package main

import "fmt"

func main() {
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
}

for循环的初始语句可以被忽略,但是用作与隔开的分号;还是要有的。

package main

import "fmt"

var i = 0

func main() {
	for ; i < 10; i++ {
		fmt.Println(i)
	}
}

for循环的结束语句也是可以省略掉的

package main

import "fmt"

var i = 0

func main() {
	for i < 10 {
		fmt.Println(i)
		i++
	}
}

for range(键值循环)

键值循环可以用作与遍历,切片,map还有channel。遵循一下规律

  1. 数组、切片、字符串返回索引和值。
  2. map返回键和值。
  3. 通道(channel)只返回通道内的值。

switch

goto(跳转指定标签)

goto的使用可以这么理解,在一个循环中当运行到某一个位置时我不想让他继续运行下去而是跳到其他的地方,这个时候就可以使用goto,用来简化一些代码的实现过程。

func gotoDemo1() {
	var breakFlag bool
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// 设置退出标签
				breakFlag = true
				break
			}
			fmt.Printf("%v-%v\n", i, j)
		}
		// 外层for循环判断
		if breakFlag {
			break
		}
	}
}

使用goto语句能简化代码:

func gotoDemo2() {
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// 设置退出标签
				goto breakTag
			}
			fmt.Printf("%v-%v\n", i, j)
		}
	}
	return
	// 标签
breakTag:
	fmt.Println("结束for循环")
}

break

跳出循环

continue

继续下次循环

数组

数组定义

var 变量名 [元素数量]T
var a [3]int
var b [4]string

数组初始化

package main

import "fmt"

func main() {

	var testArray [3]int
	var numArray = [3]int{1, 2, 3}
	var strArray = [3]string{"a", "b", "c"}
	fmt.Println(testArray)
	fmt.Println(numArray)
	fmt.Println(strArray)
}

使用[...]可以根据初始值自动判断元素有多少

package main

import "fmt"

func main() {

	var testArray [3]int
	var numArray = [...]int{1, 2, 3,4,5}
	var strArray = [...]string{"a", "b", "c", "d","f","v"}
	fmt.Println(testArray)
	fmt.Println(numArray)
	fmt.Println(strArray)
}

数组遍历

for循环遍历

package main

import "fmt"

func main() {

	var testArray [3]int
	var numArray = [...]int{1, 2, 3, 4, 5}
	var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
	fmt.Println(testArray)
	fmt.Println(numArray)
	fmt.Println(strArray)
	for i := 0; i < len(numArray); i++ {
		fmt.Println(i)
	}
	for v := 0; v < len(strArray); v++ {
		fmt.Println(strArray[v])

	}
}

for range遍历

package main

import "fmt"

func main() {

	var testArray [3]int
	var numArray = [...]int{1, 2, 3, 4, 5}
	var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
	fmt.Println(testArray)
	fmt.Println(numArray)
	fmt.Println(strArray)
	for i, v := range numArray {
		fmt.Println(i, v)
	}
	fmt.Println("----------------------------------------------------")
	for m, n := range strArray {
		fmt.Println(m, string(n))
	}
}

运行结果可以明显看出他两个是有区别的。

for循环遍历的结果并没有把角标打印出来,而是直接出的结果。

for range循环遍历的结果时把对应的角标也打印了出来,所以我们可以改善一下,使用匿名变量可以让for range的结果for循环的结果一样。

package main

import "fmt"

func main() {

	var testArray [3]int
	var numArray = [...]int{1, 2, 3, 4, 5}
	var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
	fmt.Println(testArray)
	fmt.Println(numArray)
	fmt.Println(strArray)
	for _, v := range numArray {
		fmt.Println(v)
	}
	fmt.Println("----------------------------------------------------")
	for _, n := range strArray {
		fmt.Println(string(n))
	}
}

同样的原理,可以对多为数组进行遍历。

切片

切片跟数组很像,但是切片相当于在数组类型的基础上做了一层封装,相较于数组可以更快的操作一块数据集合。

切片定义

var 变量名 [元素个数]T

可以看到他的定义方式跟数组一模一样,所以他的初始化也是一样的。

切片长度和容量

用内置函数len(),cap()可以求出切片的长度容量

package main

import "fmt"

func main() {

	var test1Slice [3]int
	fmt.Println(len(test1Slice))
	fmt.Println(cap(test1Slice))
	var test2Slice = [...]string{"a", "b", "c", "d", "e", "f"}
	fmt.Println(len(test2Slice))
	fmt.Println(cap(test2Slice))
}

表达式

切片表达式中有low和high两个定义来表示切片中的界限值

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	s := a[1:3]  // s := a[low:high]
	fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
}
a[2:]  // 等同于 a[2:len(a)]
a[:3]  // 等同于 a[0:3]
a[:]   // 等同于 a[0:len(a)]

make()函数构造切片

make([]T,元素数量,切片容量)

example:

func main(){
	a := make([]int,2,10)
}

参考

判断切片是否为空

一定要使用len(s)==0来判断,不能使用nil比较来判断。

append()方法添加元素

append()方法为切片同台添加元素,可以一次添加一个或者多个,还可以添加另一个其他切片中的元素。

package main

import "fmt"

func main() {
	var s []int
	s = append(s, 1)
	s = append(s, 2, 3, 4)
	s2 := []int{2, 3, 4}
	s = append(s, s2...)
	fmt.Println(s)
}

map使用

map:是一种无序的基于key-value的数据结构,必须初始化才可以使用。

map定义

map[keyType]valueType
  • KeyType:表示键的类型。
  • ValueType:表示键对应的值的类型。

map初始化

map默认初始值为nil,使用make()函数分配内存初始化:

make(map[keyType]valueType,[cap])

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

map使用

package main

import "fmt"

func main() {

	sourceMap := make(map[string]int, 20)
	sourceMap["A2rcher"] = 20
	sourceMap["emo"] = 30
	fmt.Println(sourceMap)
	fmt.Printf("type  %T\n", sourceMap)

}

map可以在声明的时候就填充元素:

package main

import "fmt"

func main() {

	sourceMap := make(map[string]int, 20)
	sourceMap["A2rcher"] = 20
	sourceMap["emo"] = 30
	fmt.Println(sourceMap)
	fmt.Printf("type  %T\n", sourceMap)
	fmt.Println("***************")
	sourceTest := map[string]int{
		"lucher": 20,
		"xs":10,
	}
	fmt.Println(sourceTest)


}

判断键值对是否存在

package main

import "fmt"

func main() {

	sourceMap := make(map[string]int, 20)
	sourceMap["A2rcher"] = 20
	sourceMap["emo"] = 30
	fmt.Println(sourceMap)
	fmt.Printf("type  %T\n", sourceMap)
	fmt.Println("***************")
	sourceTest := map[string]int{
		"lucher": 20,
		"xs":     10,
	}
	fmt.Println(sourceTest)
	fmt.Printf("type %T\n", sourceTest)

	fmt.Println("______________________________")
	v, ok := sourceMap["emo"]
	if !ok {
		fmt.Println("查无此人")
	} else {
		fmt.Println(v)
	}

}

map遍历

for range遍历

package main

import "fmt"

func main() {

	sourceMap := make(map[string]int, 20)
	sourceMap["a"] = 90
	sourceMap["b"] = 100
	sourceMap["c"] = 60

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

如果只想遍历前面的key时,可以把v省略。但要是想遍历value时,就需要匿名变量的帮助了。

package main

import "fmt"

func main() {

	sourceMap := make(map[string]int, 20)
	sourceMap["赵"] = 90
	sourceMap["钱"] = 100
	sourceMap["孙"] = 60

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

	}
}

delete()函数删除键值对

元素为map类型的切片

这个可以理解为在切片里面,各个元素得类型是map。

例如:

var a = make([]map[string]int,3)

值为切片类型的map

这个可以理解为在map函数里面key值是切片。

例如:

var a1 = make(map[string][]int,3)

函数

函数.最重要的一部分

函数定义

func 函数名(参数)(返回值){
	函数体
}

example:

func TestFunction(x int,y,int)int{
	return x + y
}

对于参数部分如果两个参数类型是一样的可以简化

func TestFunction(x,y int)int{
	return x + y
}

可变参数

可变参数就是参数数量不固定,参考数组...

返回值

Go语言中函数支持多返回值,函数如果有多个返回值时必须用()将所有返回值包裹起来。

func calc(x, y int) (int, int) {
	sum := x + y
	sub := x - y
	return sum, sub
}

返回值命名

函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。

例如:

func calc(x, y int) (sum, sub int) {
	sum = x + y
	sub = x - y
	return
}

返回值补充

当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。

func someFunc(x string) []int {
	if x == "" {
		return nil // 没必要返回[]int{}
	}
	...
}

全局变量

在函数外定义的变量,函数内可以访问到全局变量

局部变量

在函数内定义的变量,只能在函数内访问得到。

注意:

如果局部变量和全局变量成名,有限访问局部变量。

定义函数类型

type 函数类型 func(int ,int) int

函数作为参数

package main

import "fmt"

func add(x, y int) int {
	return x + y

}

func calc(x, y int, op func(int, int) int) int {
	return op(x, y)
}
func main() {
	ret2 := calc(10, 20, add)
	fmt.Println(ret2)

}

函数作为返回值

func do(s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}

匿名函数

匿名函数就是没有名字的函数,可以把函数赋值给变量,也可以把函数作为返回值。

func(参数)(返回值){
	函数体

}

匿名函数没有办法像普通函数那样子调用,他需要保存在某一个变量中(就是赋值),然后在执行。

package main

import "fmt"

func test() {
	func () {
		fmt.Println("匿 名 函 数")
	}()
}
func main() {
	test()
}


匿名函数执行:

func test() {
	func () {
		fmt.Println("匿 名 函 数")
	}()//在后面加上括号就相当于执行
}

或者赋值:

package main

import "fmt"

func test() func() {
	return func() {
		fmt.Println("匿 名 函 数")
	}
}
func main() {
	r := test()
	r()

}

闭包

闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境

package main

import "fmt"

//闭包简单示例  //闭包概念 闭包=函数+外层变量的引用

func test() func() {
	name := "A2rcher"
	return func() {
		fmt.Println("匿 名 函 数",name) // 如果在匿名函数中找不到调用的变量,他就会向外层去找
	}									//这个外层变量并不是全局变量,而是函数test()中的局部变量
}
func main() {
	r := test() // r引用了函数test()中的变量还有匿名函数 ,可以说r此时就是一个闭包
	r()

}

闭包还可以这么写(比较典型的一个例子)

package main

import "fmt"

//闭包简单示例  //闭包概念 闭包=函数+外层变量的引用

func test(name string) func() {

	return func() {
		fmt.Println("匿 名 函 数", name) 
	}
}
func main() {
	r := test("A2rcher") 
	r()

}

example:

两个很经典的例子 [参考博客](Go语言基础之函数 | 李文周的博客 (liwenzhou.com))

panic & recover & defer

panic/recover:可以理解为异常处理模式(但是Go语言中并没有异常处理机制,只是这样方便理解),

package main

import "fmt"

//panic and recover

func a() {
	fmt.Println("func is a")
}

//recover必须配合defer使用,而且defer一定要在panic前定义。
func b() {
	defer func() {
		err := recover()
		if err != nil { //如果err不等于nil,说明这个程序出错
			fmt.Println("func b is err ")
		}
	}()
	panic("func is b")
}

func c() {
	fmt.Println("func is c")
}

func main() {
	a()
	b()
	c()

}

程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来,继续往后执行。

内置函数

内置函数 介绍
close 主要用来关闭channel
len 用来求长度,比如string、array、slice、map、channel
new 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
append 用来追加元素到数组、slice中
panic和recover 用来做错误处理

指针

Go语言中指针没有偏移跟运算,只需要搞清楚三个概念和记住两个符号:指针地址,指针类型,指针取值,&(取地址)和*(根据地址取值)。。

贴个链接吧,大佬还是大佬

结构体

类型别名和自定义类型

type定义关键字类型

type myInt int
//将myInt定义成int类型

结构体定义

type 类型名 struct{
	字段名 字段类型
	字段名 字段类型
	...
}

由于Go语言不是面向对象编程语言,是一个面向接口的变成语言,所以他本身不想java那样有多态继承等关系,在Go语言中通过struct来实现面向对象的。(struct YYDS!)

  • 类型名:标识自定义结构体的名称,在同一个包内不能重复。
  • 字段名:表示结构体字段名。结构体中的字段名必须唯一。
  • 字段类型:表示结构体字段的具体类型。

很简单的例子:

type Person struct{
	name string
	address string
	age int
	sex string
}

自定义一个Person类型,他有name,address,age,sex四个字段分别代表个自内容。如果我要使用Person中某一个字段,我可以直接调用Person就行了。

结构体实例化

结构体实例化后才可以分配内存使用(实例化后才可以使用自定义类型)

由于结构体本身就是一共类型,所以在声明的时候可以像声明变量一样声明结构体

var 结构体实例 结构体类型

example:

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func main() {

	var p1 Person
    //通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。
	p1.name = "A2rcher"
	p1.age = 20

	fmt.Println(p1)
}

匿名结构体

匿名结构体是用来处理一些临时的数据,比如说我现在A,B两个函数,但是我临时需要使用c数据,这个时候可以用到匿名结构体。

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func main() {

	var p1 Person
	p1.name = "A2rcher"
	p1.age = 20

	var user struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}
	user.Age = 30
	user.Name = "emo"

	fmt.Println(p1)
	fmt.Println(user)

}

指针类型结构体

指针类型结构体是绝大部分情况下用到的,可以通过new关键字对结构体进行实例化得到结构体的地址。(new跟make的区别以及使用的场景参考函数中的内置函数以及指针部分)

方法和接收者

接口

并发

原文地址:https://www.cnblogs.com/zjhzjhhh/p/15091174.html