Go基础第一天就只学了这些,做个笔记

Go基础第一天就只学了这些,做个笔记,第1张

文章目录 Go语言标准库文档标识符与关键字变量常量iota基本数据类型if 语句for循环switch case数组切片map函数defer 语句panic/recover

Go语言标准库文档 标识符与关键字

字母数字下划线_, 并且只能字母和_ 开头
go语言同python一样,每个语句的结尾可以不用加分号,因为编译器会在结尾自动加上

变量

标准声明:var 变量名 类型 , var demo int
批量声明:
var (
a string
b int
c bool

变量的初始化
go会自动的对每个变量初始化类型默认的初始值,整形和浮点型默认 0 ,字符串默认 空字符串" ",布尔型默认 False, 指针、切片、映射、通道、函数和接口的零值则是 nil
var demo int = 3
var name , age = “asda”,18
短变量
go中可以使用 " := " 的形式来赋值,其次我们也可以省略掉类型,go会自动推导类型并初始化
demo := 3
全局变量和局部变量
同导包过程一样定义在main方法外面的变量,称作全局变量,而在函数,for循环,等结构语句中就是局部变量,都有各自的一个作用域
匿名元变量
比如迭代时,不想要某个值就可以使用 “ _ ” 来接收忽略掉这个值
匿名变量不占用命名空间,不分配内存,所以也就不存在重复声明
注意
1、函数外的每个语句都必须以关键字开始(var、const、func等)
2、:=不能使用在函数外。
3、_多用于占位,表示忽略值。

常量

常量同变量类似,只是把var关键字换成了const
常量定义时必须赋初始值

iota

这个东西是go语言的常量计数器,只能在常量表达式中使用
在const关键字出现时将被重置为0。
const中每新增一行常量声明将使 iota 计数一次(iota可理解为const语句块中的行索引)。

const (
		n1 = iota //0
		n2        //1
		_		  // _ 表示跳过 2
		n3        //3
		n4        //4
	)
const (
		n1 = iota //0
		n2 = 100  //100	中间插队
		n3 = iota //2
		n4        //3
	)
const n5 = iota //0
const (
		a, b = iota + 1, iota + 2 //1,2
		c, d                      //2,3
		e, f                      //3,4
	)
基本数据类型

这一块同其他高级语言是一样的
go也是强类型的语言

整型分为以下两个大类:
按长度分为:int8、int16、int32、int64
对应的无符号整型:uint8、uint16、uint32、uint64
注意
uint8 就是我们熟知的byte型
int16 对应C语言中的short型
int64 对应C语言中的long型

浮点型:float32float64

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

字符串
go 和 其他语言不一样的地方,双引号 " " 包起来的才是字符串,单引号 ’ ’ 包起来的是字符,一定要注意区别,错了会报错就很烦
然后就是使用多行字符串时用反引号包起来 ,python中是三对引号

提一下 rune 类型
前面说了单引号包起来的是字符,go语言中有两种表示
uint8类型,或者叫 byte 型,代表了ASCII码的一个字符。
rune类型,代表一个 UTF-8字符。
go使用 rune 类型来处理Unicode字符,字符串更好的扩展性,==rune 类型实际上是一个 int32 ==

if 语句

常规的格式
if (表达式1) {
} else if (表达式2){
} else {
}
加执行语句的if
if score := 65; score >= 90 {
fmt.Println(“A”)
} else if score > 75 {
fmt.Println(“B”)
} else {
fmt.Println(“C”)
}

for循环

go语言中是没有 while 的,一切的循环语句都可以通过 for 来完成
常规格式
for i := 0; i < 10; i++ {
fmt.Println(i)
}
当然也可以忽略掉初始语句和结束语句,只保留条件语句
其实就类似 while(条件语句)的格式
无限循环
for {
循环体语句
}
可以通过break、goto、return、panic语句强制打断
for range 键值循环
遍历数组、切片、字符串、map 及通道,返回索引和值,有点像python了

a := [...]byte{'a', 'b', 'c', 'd'}
for key, value := range a {
	fmt.Printf("%d %c\n", key, value)
}
switch case

同C语言一样,直接跳过

数组

var d
emo [10]int
前面讲过不赋值会自动初始化 0
var demo = [5]int{1,2,3,4,5}
...go自己推导数组长度
var demo = […]int{2,3,4,6,7,6,8,3}
初始化时可以加下标索引
var demo = […]byte{1:‘a’,2:‘b’,4:‘c’} [0,a,b,0,c]
二维数组

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s\t", v2)
		}
		fmt.Println()
	}
}
切片

因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性
切片是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。非常灵活,支持自动扩容
var demo []int
定义方式和数组不同的就是没有提前给出长度
使用也类似于python的切片

a := [...]byte{'a', 'b', 'c', 'd'}
var src []byte
src = a[1:3]
fmt.Printf("%c", src)

上面都是基于数组来创建的切片
动态切片,就需要使用内置的make()函数
下面例子a 的内部存储空间分配了10个,但实际上只用了2个。
容量并不会影响当前元素的个数,所以len(a)返回2,cap(a)则返回该切片的容量。

a := make([]int, 2, 10)
fmt.Println(a)      //[0 0]
fmt.Println(len(a)) //2
fmt.Println(cap(a)) //10

切片赋值拷贝

s1 := make([]int, 3) //[0 0 0]
s2 := s1  //将s1直接赋值给s2,s1和s2共用一个底层数组
s2[0] = 100
fmt.Println(s1) //[100 0 0]
fmt.Println(s2) //[100 0 0]
//两个变量共享底层数组,指向同一块内存地址

append() 切片添加元素

var s []int
s = append(s, 1)        // [1]
s = append(s, 2, 3, 4)  // [1 2 3 4]
s2 := []int{5, 6, 7}  
s = append(s, s2...)    // [1 2 3 4 5 6 7]
//一个切片中添加另一个切片中的元素,后面要加 ...

copy() 复制切片

a := []int{1, 2, 3, 4, 5}
c := make([]int, 5, 5)
copy(c, a) //使用copy()函数将切片a中的元素复制到切片c
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1 2 3 4 5]
c[0] = 1000
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1000 2 3 4 5]

切片中删除元素
go语言中没有删除切片元素的函数方法

a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
// 跳过下标为2的元素不append加入新切片
fmt.Println(a) // [30 31 33 34 35 36 37]
map

map是一种无序的基于key-value的数据结构,
go语言中的map是引用类型,必须初始化才能使用。
map类型的变量默认初始值为nil,需要使用make()函数来分配内存
map[键数据类型] 值数据类型

scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])

user := map[string]int{
	"张三":1,
	"李四":2,
	"王五":3
}

判断某个键是否存在
value , ok := map[key]

// 如果key存在ok为true,v为对应的值
// 不存在ok为false,v为值类型的零值
v, ok := user["张三"]
if ok {
	fmt.Println(v)
} else {
	fmt.Println("查无此人")
}

delete() 函数删除键值对
delete(map,key)

值为map类型的切片

func main() {
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "张三"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "123@123.com"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
}

值为切片类型的map

func main() {
	var sliceMap = make(map[string][]string, 3)
	fmt.Println(sliceMap)
	fmt.Println("after init")
	key := "中国"
	value, ok := sliceMap[key]
	if !ok {
		value = make([]string, 0, 2)
	}
	value = append(value, "北京", "上海")
	sliceMap[key] = value
	fmt.Println(sliceMap)
}
函数

func 函数名(参数) (返回值类型){
函数体
}
参数和返回值类型可以不是必须的,没有参数和返回值

可变参数
可变参数是指函数的参数数量不固定。go语言中的可变参数通过在参数名后加 ... 来标识
可变参数要作为函数的最后一个参数

func intSum(x ...int) int {
	fmt.Println(x)	// 传入的 x 是一个切片
	sum := 0
	for _, v := range x {
		sum = sum + v
	}
	return sum
}

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

函数类型定义 和 函数类型变量

type calculation func(int, int) int
func add(x, y int) int {
	return x + y
}
func main() {
	var c calculation
	c = add
	fmt.Println(c(2, 3))
	f := add
	fmt.Println(f(2,3))
}

函数作为参数

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 f(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 main() {
	// 将匿名函数保存到变量
	add := func(x, y int) {
		fmt.Println(x + y)
	}
	add(10, 20) // 通过变量调用匿名函数
	
	//自执行函数:匿名函数定义完加()直接执行
	func(x, y int) {
		fmt.Println(x + y)
	}(10, 20)
}

闭包
闭包指的是一个函数和与其相关的引用环境组合而成的实体。
简单来说,闭包 = 函数+引用环境
变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包
在 f 的生命周期内,变量 x 也一直有效

func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder()
	fmt.Println(f(10)) //10
	fmt.Println(f(20)) //30
	fmt.Println(f(30)) //60

	f1 := adder()
	fmt.Println(f1(40)) //40
	fmt.Println(f1(50)) //90
}

闭包进阶1

func adder2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder2(10)
	fmt.Println(f(10)) //20
	fmt.Println(f(20)) //40
	fmt.Println(f(30)) //70

	f1 := adder2(20)
	fmt.Println(f1(40)) //60
	fmt.Println(f1(50)) //110
}

闭包进阶2

func makeSuffixFunc(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}

func main() {
	jpgFunc := makeSuffixFunc(".jpg")
	txtFunc := makeSuffixFunc(".txt")
	fmt.Println(jpgFunc("test")) //test.jpg
	fmt.Println(txtFunc("test")) //test.txt
}

闭包进阶3

func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base += i
		return base
	}

	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}

func main() {
	f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}
defer 语句

go语言中的defer语句会将其后面跟随的语句进行延迟处理
在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行
也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行
用法类似于Java中的 finally 语句,一般用于释放某些已分配的资源

func main() {
	fmt.Println("start")
	defer fmt.Println(1)
	defer fmt.Println(2)
	defer fmt.Println(3)
	fmt.Println("end")
}
start
end
3
2
1

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题
比如:资源清理、文件关闭、解锁及记录时间

defer执行时机
go语言中函数 return 在底层并不是原子 *** 作,它分为返回值赋值和RET指令两步
而 defer 语句执行的时机就在返回值赋值 *** 作之后,RET指令执行之前

defer 一道经典实例

func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x
// 返回值未命名,所以返回值=5,defer 是对x再 *** 作,不影响返回值,结果 5
}
func f2() (x int) {
	defer func() {
		x++
	}()
	return 5
// 返回值命名x, 所以返回值=x=5,defer 对x再 *** 作,结果 6
}
func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
// 返回值命名y=5, defer 对x再 *** 作,不影响返回值,结果 5
}
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5
// 返回值命名x=5,defer 后函数中传入的是形参x,不影响x的值,结果 5
}
func main() {
	fmt.Println(f1())
	fmt.Println(f2())
	fmt.Println(f3())
	fmt.Println(f4())
}

defer 一道面试题
可以发现外围函数会被延时,函数里面的参数不影响

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}
func main() {
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20
}
A 1 2 3
B 10 2 12  
BB 10 12 22
AA 1 3 4  
panic/recover

go语言没有结构化异常,使用 panic 抛出错误,recover 捕获错误
go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
panic 可以在任何地方引发,但 recover 只有在 defer 调用的函数中有效

func funcA() {
	fmt.Println("func A")
}
func funcB() {
	defer func() {
		err := recover()
	   //如果程序出现了panic错误,可以通过recover恢复
		if err != nil {
			fmt.Println("recover in B")
		}
	}()
	panic("panic in B")
}
func funcC() {
	fmt.Println("func C")
}
func main() {
	funcA()
	funcB()
	funcC()
}

func A      
recover in B
func C   

注意两点
1、recover() 必须搭配defer使用
2、defer 一定要在可能引发panic的语句之前定义

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/994060.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存