Go学习笔记3
九、错误处理1.defer+recover机制处理异常错误
展示错误:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233734931-697607634.png 发现:程序中出现错误/恐慌以后,程序被中断,无法继续执行。
错误处理/捕获机制:
内置函数recover:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233734910-2022130242.png https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735208-822894550.png 2.自定义错误
需要调用errors包下的New函数:函数返回error类型
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735054-337967038.png https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735592-1130484412.png 3.panic
有一种情况:程序出现错误以后,后续代码就没有必要执行,想让程序中断,退出程序:
借助:builtin包下内置函数:panic
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233734950-1781834691.png https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735595-343529575.png十、数组
1.使用
数组定义格式:
var 数组名 [数组大小]数据类型
例如:
var scores intpackage main
import "fmt"
func main(){
//实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
//给出五个学生的成绩:--->数组存储:
//定义一个数组:
var scores int
//将成绩存入数组:
scores = 95
scores = 91
scores = 39
scores = 60
scores = 21
//求和:
//定义一个变量专门接收成绩的和:
sum := 0
for i := 0;i < len(scores);i++ {//i: 0,1,2,3,4
sum += scores
}
//平均数:
avg := sum / len(scores)
//输出
fmt.Printf("成绩的总和为:%v,成绩的平均数为:%v",sum,avg)
}2.内存分析
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735370-593882382.png赋值内存(数组是值类型,在栈中开辟内存)
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735391-921636106.png3.数组的遍历
【1】普通for循环
【2】键值循环
(键值循环) for range结构是Go语言特有的一种的迭代结构,在许多情况下都非常有用,for range 可以遍历数组、切片、字符串、map 及通道,for range 语法上类似于其它语言中的 foreach 语句,一般形式为:
for key, val := range coll {
...
}注意:
(1)coll就是你要的数组
(2)每次遍历得到的索引用key接收,每次遍历得到的索引位置上的值用val
(3)key、value的名字随便起名k、v key、value
(4)key、value属于在这个循环中的局部变量
(5)你想忽略某个值:用_就可以了:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735321-2107501213.pngpackage main
import "fmt"
func main(){
//实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
//给出五个学生的成绩:--->数组存储:
//定义一个数组:
var scores int
//将成绩存入数组:(循环 + 终端输入)
for i := 0; i < len(scores);i++ {//i:数组的下标
fmt.Printf("请录入第个%d学生的成绩",i + 1)
fmt.Scanln(&scores)
}
//展示一下班级的每个学生的成绩:(数组进行遍历)
//方式1:普通for循环:
for i := 0; i < len(scores);i++ {
fmt.Printf("第%d个学生的成绩为:%d\n",i+1,scores)
}
fmt.Println("-------------------------------")
//方式2:for-range循环
for key,value := range scores {
fmt.Printf("第%d个学生的成绩为:%d\n",key + 1,value)
}
}结果:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735597-470195411.png 4.数组的初始化操作
package main
import "fmt"
func main(){
//第一种:
var arr1 int = int{3,6,9}
fmt.Println(arr1)
//第二种:
var arr2 = int{1,4,7}
fmt.Println(arr2)
//第三种:
var arr3 = [...]int{4,5,6,7}
fmt.Println(arr3)
//第四种:
var arr4 = [...]int{2:66,0:33,1:99,3:88}
fmt.Println(arr4)
}5.注意事项
【1】长度属于类型的一部分 :
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735415-1673253130.png 【2】Go中数组属值类型,在默认情况下是值传递,因此会进行值拷贝。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735469-361555717.png【3】如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735439-2054368579.png6.二维数组
二维数组有初始值,
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735410-1372057408.png初始化:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735360-1631857302.png遍历
package main
import "fmt"
func main(){
//定义二维数组:
var arr int = int{{1,4,7},{2,5,8},{3,6,9}}
fmt.Println(arr)
fmt.Println("------------------------")
//方式1:普通for循环:
for i := 0;i < len(arr);i++{
for j := 0;j < len(arr);j++ {
fmt.Print(arr,"\t")
}
fmt.Println()
}
fmt.Println("------------------------")
//方式2:for range循环:
for key,value := range arr {
for k,v := range value {
fmt.Printf("arr[%v][%v]=%v\t",key,k,v)
}
fmt.Println()
}
}十一、切片
【1】切片(slice)是golang中一种特有的数据类型
【2】数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在 Go 语言的代码里并不是特别常见。相对的切片却是随处可见的,切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。
【3】切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
1.语法
var 切片名 []类型 = 数组的一个片段引用https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735707-1704396510.png 2.内存分析
切片有3个字段的数据结构:一个是指向底层数组的指针,一个是切片的长度,一个是切片的容量。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735457-1392663811.png 3.定义
【1】方式1:定义一个切片,然后让切片去引用一个已经创建好的数组。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735464-776432898.png 【2】方式2:通过make内置函数来创建切片。基本语法: var切片名, len,)
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735595-313733691.png make底层创建一个数组,对外不可见,所以不可以直接操作这个数组,要通过slice去间接的访问各个元素,不可以直接对数组进行维护/操作
【3】方式3:定一个切片,直接就指定具体数组,使用原理类似make的方式。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735267-767947595.png
4.遍历
【1】方式1:for循环常规方式遍历
【2】方式2:for-range 结构遍历切片
package main
import "fmt"
func main(){
//定义切片:
slice := make([]int,4,20)
slice = 66
slice = 88
slice = 99
slice = 100
//方式1:普通for循环
for i := 0;i < len(slice);i++ {
fmt.Printf("slice[%v] = %v \t" ,i,slice)
}
fmt.Println("\n------------------------------")
//方式2:for-range循环:
for i,v := range slice {
fmt.Printf("下标:%v ,元素:%v\n" ,i,v)
}
}5.注意事项
【1】切片定义后不可以直接使用,需要让其引用到一个数组,或者make一个空间供切片来使用
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735599-1470814385.png 【2】切片使用不能越界。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735593-1979904088.png【3】简写方式:
[*]var slice = arr----》 var slice = arr[:end]
[*]var slice = arr----》var slice = arr
[*]var slice = arr ----》 var slice = arr[:]
【4】切片可以继续切片
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735496-1590209930.png
【5】切片可以动态增长
package main
import "fmt"
func main(){
//定义数组:
var intarr int = int{1,4,7,3,6,9}
//定义切片:
var slice []int = intarr //4,7,3
fmt.Println(len(slice))
slice2 := append(slice,88,50)
fmt.Println(slice2) //
fmt.Println(slice)
//底层原理:
//1.底层追加元素的时候对数组进行扩容,老数组扩容为新数组:
//2.创建一个新数组,将老数组中的4,7,3复制到新数组中,在新数组中追加88,50
//3.slice2 底层数组的指向 指向的是新数组
//4.往往我们在使用追加的时候其实想要做的效果给slice追加:
slice = append(slice,88,50)
fmt.Println(slice)
//5.底层的新数组 不能直接维护,需要通过切片间接维护操作。
}可以通过append函数将切片追加给切片:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735392-1949510066.png
【6】切片的拷贝:
package main
import "fmt"
func main(){
//定义切片:
var a []int = []int{1,4,7,3,6,9}
//再定义一个切片:
var b []int = make([]int,10)
//拷贝:
copy(b,a) //将a中对应数组中元素内容复制到b中对应的数组中
fmt.Println(b)
}十二、映射
1.map的引入
【1】映射(map), Go语言中内置的一种类型,它将键值对相关联,我们可以通过键 key来获取对应的值 value。 类似其它语言的集合
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735598-818708310.png
【2】基本语法
var map变量名 mapvaluetypekey、value的类型:bool、数字、string、指针、channel 、还可以是只包含前面几个类型的接口、结构体、数组
key通常为int 、string类型,value通常为数字(整数、浮点数)、string、map、结构体
key:slice、map、function不可以
【3】代码:
map的特点:
(1)map集合在使用前一定要make
(2)map的key-value是无序的
(3)key是不可以重复的,如果遇到重复,后一个value会替换前一个value
(4)value可以重复的
package main
import "fmt"
func main(){
//定义map变量:
var a mapstring
//只声明map内存是没有分配空间
//必须通过make函数进行初始化,才会分配空间:
a = make(mapstring,10) //map可以存放10个键值对
//将键值对存入map中:
a = "张三"
a = "李四"
a = "王五"
a = "朱六"
a = "张三"
//输出集合
fmt.Println(a)
}2.创建
package main
import "fmt"
func main(){
//方式1:
//定义map变量:
var a mapstring
//只声明map内存是没有分配空间
//必须通过make函数进行初始化,才会分配空间:
a = make(mapstring,10) //map可以存放10个键值对
//将键值对存入map中:
a = "张三"
a = "李四"
//输出集合
fmt.Println(a)
//方式2:
b := make(mapstring)
b = "张三"
b = "李四"
fmt.Println(b)
//方式3:
c := mapstring{
20095452 : "张三",
20098765 : "李四",
}
c = "王五"
fmt.Println(c)
}3.操作
1】增加和更新操作:
map["key"]= value——》 如果key还没有,就是增加,如果key存在就是修改。
【2】删除操作:
delete(map,"key") , delete是一个内置函数,如果key存在,就删除该key-value,如果k的y不存在,不操作,但是也不会报错
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735376-684476778.png
【3】清空操作:
(1)如果我们要删除map的所有key ,没有一个专门的方法一次删除,可以遍历一下key,逐个删除
(2)或者map = make(...),make一个新的,让原来的成为垃圾,被gc回收
【4】查找操作:
value ,bool = map
value为返回的value,bool为是否返回 ,要么true 要么false
package main
import "fmt"
func main(){
//定义map
b := make(mapstring)
//增加:
b = "张三"
b = "李四"
//修改:
b = "王五"
//删除:
delete(b,20095387)
delete(b,20089546)
fmt.Println(b)
//查找:
value,flag := b
fmt.Println(value)
fmt.Println(flag)
}【5】获取长度:len函数
【6】遍历:for-range
package main
import "fmt"
func main(){
//定义map
b := make(mapstring)
//增加:
b = "张三"
b = "李四"
b = "王五"
//获取长度:
fmt.Println(len(b))
//遍历:
for k,v := range b {
fmt.Printf("key为:%v value为%v \t",k,v)
}
fmt.Println("---------------------------")
//加深难度:
a := make(mapmapstring)
//赋值:
a["班级1"] = make(mapstring,3)
a["班级1"] = "露露"
a["班级1"] = "丽丽"
a["班级1"] = "菲菲"
a["班级2"] = make(mapstring,3)
a["班级2"] = "小明"
a["班级2"] = "小龙"
a["班级2"] = "小飞"
for k1,v1:= range a {
fmt.Println(k1)
for k2,v2:= range v1{
fmt.Printf("学生学号为:%v 学生姓名为%v \t",k2,v2)
}
fmt.Println()
}
}十三、对象
1.对象的引入
【1】Golang语言面向对象编程说明:
(1)Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
(2)Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Gelang是基于struct来实现OOP特性的。
(3)Golang面向对象编程非常简洁,去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等
(4)Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
【2】结构体的引入:
具体的对象:
一位老师:郜宇博老师: 姓名:郜宇博 年龄:22岁 性别 :男 ......
可以使用变量来处理:
package main
import "fmt"
func main(){
//郜宇博老师: 姓名:郜宇博 年龄:22岁 性别 :男 ......
var name string = "郜宇博"
var age int = 22
var sex string = "男"
//马士兵老师:
var name2 string = "马士兵"
var age2 int = 45
var sex2 string = "男"
}缺点:
(1)不利于数据的管理、维护
(2)老师的很多属性属于一个对象,用变量管理太分散了
2.结构体
代码:
package main
import "fmt"
//定义老师结构体,将老师中的各个属性统一放入结构体中管理:
type Teacher struct{
//变量名字大写外界可以访问这个属性
Name string
Age int
School string
}
func main(){
//创建老师结构体的实例、对象、变量:
var t1 Teacher // var a int
fmt.Println(t1) //在未赋值时默认值:{ 0 }
t1.Name = "马士兵"
t1.Age = 45
t1.School = "清华大学"
fmt.Println(t1)
fmt.Println(t1.Age + 10)
}3.结构体的创建
1.直接创建
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735595-1305916094.png 2.附带初始值
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735751-337257948.png3.结构体指针创建
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735584-1055352415.png https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735951-143298109.png4.结构体之间转换
【1】结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
package main
import "fmt"
type Student struct {
Age int
}
type Person struct {
Age int
}
func main(){
var s Student = Student{10}
var p Person = Person{10}
s = Student(p)
fmt.Println(s)
fmt.Println(p)
}【2】结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
package main
import "fmt"
type Student struct {
Age int
}
type Stu Student
func main(){
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1)
fmt.Println(s2)
}5.方法的引入
【1】方法是作用在指定的数据类型上、和指定的数据类型绑定,因此自定义类型,都可以有方法,而不仅仅是struct
【2】方法的声明和调用格式:
声明:
type A struct {
Num int
}
func (a A) test() {
fmt.Println(a.Num)
}调用:
var a A
a.test()(1)func (a A) test()相当于A结构体有一个方法叫test
(2)(a A)体现方法test和结构体A绑定关系
(3)代码层面:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735616-1035753655.png注意:
(1)test方法中参数名字随意起
(2)结构体Person和test方法绑定,调用test方法必须靠指定的类型:Person
(3)如果其他类型变量调用test方法一定会报错。
(4)结构体对象传入test方法中,值传递,和函数参数传递一致。
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735780-56717182.png 6.方法的注意事项
(1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735665-610876392.png
(2)如程序员希望在方法中,改变结构体变量的值,可以通过结构体指针的方式来处理
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735739-484037772.png https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735418-1600103395.png 我们写程序的时候,可以直接简化:
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735637-1697131946.png 底层编译器做了优化,底层会自动帮我们加上 &*
(3)Golang中的方法作用在指定的数据类型上的,和指定的数据类型绑定,因此自定义类型,都可以有方法,而不仅仅是struct,比如int , float32等都可以有方法
package main
import "fmt"
type integer int
func (i integer) print(){
i = 30
fmt.Println("i = ",i)
}
func (i *integer) change(){
*i = 30
fmt.Println("i = ",*i)
}
func main(){
var i integer = 20
i.print()
i.change()
fmt.Println(i)
}(4)方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
(5)如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
以后定义结构体的话,常定义String()作为输出结构体信息的方法,在fmt.Println会自动调用
package main
import "fmt"
type Student struct{
Name string
Age int
}
func (s *Student) String() string{
str := fmt.Sprintf("Name = %v , Age = %v",s.Name,s.Age)
return str
}
func main(){
stu := Student{
Name : "丽丽",
Age : 20,
}
//传入地址,如果绑定了String方法就会自动调用
fmt.Println(&stu)
}7.方法和函数的区别
【1】绑定指定类型:
方法:需要绑定指定数据类型
函数:不需要绑定数据类型
【2】调用方式不一样:
函数的调用方式:
函数名(实参列表)
方法的调用方式:变量.方法名(实参列表)
package main
import "fmt"
type Student struct{
Name string
}
//定义方法:
func (s Student) test01(){
fmt.Println(s.Name)
}
//定义函数:
func method01(s Student){
fmt.Println(s.Name)
}
func main(){
//调用函数:
var s Student = Student{"丽丽"}
method01(s)
//方法调用:
s.test01()
}【3】对于函数来说,参数类型对应是什么就要传入什么。
package main
import "fmt"
type Student struct{
Name string
}
//定义函数:
func method01(s Student){
fmt.Println(s.Name)
}
func method02(s *Student){
fmt.Println((*s).Name)
}
func main(){
var s Student = Student{"丽丽"}
method01(s)
//method01(&s)错误
method02(&s)
//method02(s)错误
}【4】对于方法来说,接收者为值类型,可以传入指针类型,接受者为指针类型,可以传入值类型。
package main
import "fmt"
type Student struct{
Name string
}
//定义方法:
func (s Student) test01(){
fmt.Println(s.Name)
}
func (s *Student) test02(){
fmt.Println((*s).Name)
}
func main(){
var s Student = Student{"丽丽"}
s.test01()
(&s).test01()//虽然用指针类型调用,但是传递还是按照值传递的形式
(&s).test02()
s.test02()//等价
}8.创建结构体时初始化
【1】方式1:按照顺序赋值操作
缺点:必须按照顺序有局限性
【2】方式2:按照指定类型
【3】方式3:想要返回结构体的指针类型
https://img2023.cnblogs.com/blog/2319323/202309/2319323-20230907233735772-61801027.png
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]