Gin入门笔记
1.基本数据类型
整型
类型 | 占用存储空间 |
---|---|
int | 32位系统4字节 64位系统8字节 |
unint | 32位系统4字节 64位系统8字节 |
int8 | 1字节 |
int16 | 2字节 |
int32 | 4字节 |
int64 | 8字节 |
uint8 | 1字节 |
uint16 | 2字节 |
uint32 | 4字节 |
uint64 | 8字节 |
rune | 4字节(等价int32) |
byte | 1字节(等价unint8) |
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。
同样byte也是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。
浮点型
类型 | 占用存储空间 |
---|---|
float32 | 4字节 |
float64 | 8字节 |
布尔型
布尔类型也叫bool类型,bool类型数据只允许取值true和false,占用一个字节
字符串
字符串也叫string类型,是一串固定长度的字符连接起来的字符序列。
字符串的表示形式:
(1)如果字符串中没有特殊字符,字符串的表示形式用双引号
(2)如果字符串中有特殊字符,字符串的表示形式用反引号 ``
常量
常量使用const声明,常量的值不可修改
所有常量的运算都可以在编译期完成,这样可以减少运行时的工作,也方便其他编译优化。当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除零、字符串索引越界、任何导致无效浮点数的操作等。
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof(§13.1)。
复数
complex64 32位浮点型数
complex128 64位浮点型数
Go语言提供了两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度。内置的complex函数用于构建复数,内建的real和imag函数分别返回复数的实部和虚部:
var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y) // "(-5+10i)"
fmt.Println(real(x*y)) // "-5"
fmt.Println(imag(x*y)) // "10"
在常量算术规则下,一个复数常量可以加到另一个普通数值常量(整数或浮点数、实部或虚部),我们可以用自然的方式书写复数,就像1+2i或与之等价的写法2i+1。上面x和y的声明语句还可以简化:
x := 1 + 2i
y := 3 + 4i
默认值
在Golang中数据类型都有一个默认值,当程序员没有赋值时,就会保留默认值(默认值又叫零值)
数据类型 | 默认值 |
---|---|
整数类型 | 0 |
浮点类型 | 0 |
布尔类 | false |
字符串类型 | “” |
类型转换
显示转换
Go在不同类型的变量之间赋值时需要显式转换,并且只有显式转换(强制转换)。
语法:
表达式T(v)将值v转换为类型T
T : 就是数据类型
v : 就是需要转换的变量
基本数据类型转换为string
方式1:fmt.Sprintf(“%参数”,表达式) —》 重点练习这个,推荐方式
package main
import "fmt"
func main(){var n1 int = 19var n2 float32 = 4.78var n3 bool = falsevar n4 byte = 'a'var s1 string = fmt.Sprintf("%d",n1)fmt.Printf("s1对应的类型是:%T ,s1 = %q \n",s1, s1)var s2 string = fmt.Sprintf("%f",n2)fmt.Printf("s2对应的类型是:%T ,s2 = %q \n",s2, s2)var s3 string = fmt.Sprintf("%t",n3)fmt.Printf("s3对应的类型是:%T ,s3 = %q \n",s3, s3)var s4 string = fmt.Sprintf("%c",n4)fmt.Printf("s4对应的类型是:%T ,s4 = %q \n",s4, s4)
}
方式2:使用strconv包的函数
package main
import("fmt""strconv"
)
func main(){var n1 int = 18var s1 string = strconv.FormatInt(int64(n1),10) //参数:第一个参数必须转为int64类型 ,第二个参数指定字面值的进制形式为十进制fmt.Printf("s1对应的类型是:%T ,s1 = %q \n",s1, s1)var n2 float64 = 4.29var s2 string = strconv.FormatFloat(n2,'f',9,64)//第二个参数:'f'(-ddd.dddd) 第三个参数:9 保留小数点后面9位 第四个参数:表示这个小数是float64类型fmt.Printf("s2对应的类型是:%T ,s2 = %q \n",s2, s2)var n3 bool = truevar s3 string = strconv.FormatBool(n3)fmt.Printf("s3对应的类型是:%T ,s3 = %q \n",s3, s3)
}
string转换为基本数据类型
使用strconv包的函数
strconv.ParseT
2.复合数据类型
数组
var 数组名 [数组大小]数据类型
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。
数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len()
函数将返回数组中元素的个数。
arr
中存的是地址值,和arr[0]
、arr[0][0]
地址相同
数组的初始化
//第一种:
var arr1 [3]int = [3]int{3,6,9}
fmt.Println(arr1)
//第二种:
var arr2 = [3]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)
注意:
- 长度属于类型的一部分
- Go中数组属值类型,在默认情况下是值传递,因此会进行值拷贝
- 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
二维数组的遍历
package main
import "fmt"
func main(){//定义二维数组:var arr [3][3]int = [3][3]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[i]);j++ {fmt.Print(arr[i][j],"\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()}
}
Slice
slice斯莱丝
一个slice由三个部分构成:指针、长度和容量。指针指向第一个slice元素对应的底层数组元素的地址,要注意的是slice的第一个元素并不一定就是数组的第一个元素。长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。内置的len和cap函数分别返回slice的长度和容量。
创建切片:
方式1:定义一个切片,然后让切片去引用一个已经创建好的数组。
sl := arr[2:4]
方式2:通过make内置函数来创建切片。基本语法: var 切片名 = make([]T, len,[cap]
sl2 := make([]int, 2, 3)
方式3:定一个切片,直接就指定具体数组,使用原理类似make的方式。\
sl3 := []int{1, 2, 3}
append函数
切片可以动态增长
package main
import "fmt"
func main(){//定义数组:var intarr [6]int = [6]int{1,4,7,3,6,9}//定义切片:var slice []int = intarr[1:4] //4,7,3fmt.Println(len(slice))slice2 := append(slice,88,50)fmt.Println(slice2) //[4 7 3 88 50]fmt.Println(slice)//底层原理://1.底层追加元素的时候对数组进行扩容,老数组扩容为新数组://2.创建一个新数组,将老数组中的4,7,3复制到新数组中,在新数组中追加88,50//3.slice2 底层数组的指向 指向的是新数组 //4.往往我们在使用追加的时候其实想要做的效果给slice追加:slice = append(slice,88,50)fmt.Println(slice)//5.底层的新数组 不能直接维护,需要通过切片间接维护操作。
}
可以通过append函数将切片追加给切片
slice3 := []int{99,44}
slice = append(slice,slice3...)
fmt.Println(slice)
切片的拷贝
//定义切片:
var a []int = []int{1,4,7,3,6,9}
//再定义一个切片:
var b []int = make([]int,10)
//拷贝:
copy(b,a) //将a中对应数组中元素内容复制到b中对应的数组中
fmt.Println(b)
注意:
切片定义后不可以直接使用,需要让其引用到一个数组,或者make一个空间供切片来使用
切片使用不能越界,如果切片操作超出cap(s)的上限将导致一个panic异常
Map
var map变量名 map[keytype]valuetype
创建方式
//方式1:
//定义map变量:
var a map[int]string
//只声明map内存是没有分配空间
//必须通过make函数进行初始化,才会分配空间:
a = make(map[int]string,10) //map可以存放10个键值对
//将键值对存入map中:
a[20095452] = "张三"
a[20095387] = "李四"
//输出集合
fmt.Println(a)
//方式2:
b := make(map[int]string)
b[20095452] = "张三"
b[20095387] = "李四"
fmt.Println(b)
//方式3:
c := map[int]string{20095452 : "张三",20098765 : "李四",
}
c[20095387] = "王五"
fmt.Println(c)
map的特点:
(1)map集合在使用前一定要make
(2)map的key-value是无序的
(3)key是不可以重复的,如果遇到重复,后一个value会替换前一个value
(4)value可以重复的
package main
import "fmt"
func main(){//定义map变量:var a map[int]string//只声明map内存是没有分配空间//必须通过make函数进行初始化,才会分配空间:a = make(map[int]string,10) //map可以存放10个键值对//将键值对存入map中:a[20095452] = "张三"a[20095387] = "李四"a[20097291] = "王五"a[20095387] = "朱六"a[20096699] = "张三"//输出集合fmt.Println(a)
}
操作
【1】增加和更新操作:
map[“key”]= value ——》 如果key还没有,就是增加,如果key存在就是修改。
【2】删除操作:
delete(map,“key”) , delete是一个内置函数,如果key存在,就删除该key-value,如果k的y不存在,不操作, 但是也不会报错
【3】清空操作:
(1)如果我们要删除map的所有key ,没有一个专门的方法一次删除,可以遍历一下key,逐个删除
(2)或者map = make(…),make一个新的,让原来的成为垃圾,被gc回收
【4】查找操作:
value ,bool = map[key]
value为返回的value,bool为是否返回 ,要么true 要么false
【5】获取长度:len函数
结构体
结构体定义
//定义老师结构体,将老师中的各个属性 统一放入结构体中管理:
type Teacher struct{//变量名字大写外界可以访问这个属性Name stringAge intSchool string
}
实例创建方式
1.直接创建
//创建老师结构体的实例、对象、变量:
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)
2.创建时赋值
var t Teacher = Teacher{"赵珊珊",31,"黑龙江大学"}
fmt.Println(t)
3.返回的是结构体指针
var t *Teacher = new(Teacher)
(*t).Name = "马士兵"
(*t).Age = 45
//为了符合程序员的编程习惯,go提供了简化的赋值方式
t.School = "清华大学"
//go编译器底层对t.School转化(*t).School = "清华大学"
fmt.Println(*t)
4.返回的是结构体指针,创建时赋值
var t *Teacher = &Teacher{"马士兵",45,"清华大学"}
fmt.Println(t)
结构体之间的转换
结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
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)
}
结构体进行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)
}
嵌入匿名结构体
在结构体中嵌入匿名结构体能继承嵌入结构体的字段和方法,从而实现oop的继承特性,提高代码复用性
组合
如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字。
3.流程控制
分支结构
if
表达式外无需小括号 ( ),而大括号 { } 则是必须的。
switch
switch 表达式 {case 值1,值2,.….:语句块1case 值3,值4,...:语句块2....default:语句块
}
无条件 switch:
无条件的 switch 同 switch true 一样。
注意:
- switch后是一个表达式(即:常量值、变量、一个有返回值的函数等都可以)
- case后面的值如果是常量值(字面量),则要求不能重复
- case后的各个值的数据类型,必须和 switch 的表达式数据类型一致
- case后面可以带多个值,使用逗号间隔。比如 case 值1,值2…
- case后面不需要带break
- default语句不是必须的,位置也是随意的。
- switch穿透,利用fallthrough关键字,如果在case语句块后增加fallthrough ,则会继续执行下一个case
循环结构
for
Go 的 for 语句后面的三个构成部分外没有小括号, 大括号 { } 则是必须的。
for 是 Go 中的「while」 分号可以去掉,和C中的while类似
如果省略循环条件,该循环就不会结束,因此无限循环可以写得很紧凑。
for range
for key, val := range coll {...
}
关键字
break
结束最近的循环
continue
结束本次循环,继续下一次循环
goto
Golang的 goto 语句可以无条件地转移到程序中指定的行。
goto语句通常与条件语句配合使用。可用来实现条件转移.
在Go程序设计中一般不建议使用goto语句,以免造成程序流程的混乱。-
return
结束当前的函数
4.函数
定义:
func function_name( [parameter list] ) [return_types] {函数体
}
闭包
Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
匿名函数是一种没有函数名的函数,通常用于在函数内部定义函数,或者作为函数参数进行传递。
以下实例中,我们创建了函数 getSequence()
,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:
package mainimport "fmt"func getSequence() func() int {i:=0return func() int {i+=1return i }
}func main(){/* nextNumber 为一个函数,函数 i 为 0 */nextNumber := getSequence() /* 调用 nextNumber 函数,i 变量自增 1 并返回 */fmt.Println(nextNumber())fmt.Println(nextNumber())fmt.Println(nextNumber())/* 创建新的函数 nextNumber1,并查看结果 */nextNumber1 := getSequence() fmt.Println(nextNumber1())fmt.Println(nextNumber1())
}//执行结果//1//2//3//1//2
5.接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。
/* 定义接口 */
type interface_name interface {method_name1 [return_type]method_name2 [return_type]method_name3 [return_type]...method_namen [return_type]
}/* 定义结构体 */
type struct_name struct {/* variables */
}/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {/* 方法实现*/
}
6.错误处理
defer+recover机制处理错误
package mainimport "fmt"func main() {test()fmt.Println("上面的除法操作执行成功")fmt.Println("正常执行下面的逻辑")
}
func test() {//利用defer+recover来捕获错误:defer后加上匿名函数的调用defer func() {//调用recover内置函数,可以捕获错误:err := recover()//如果没有捕获错误,返回值为零值:nilif err != nil {fmt.Println("错误已捕获")fmt.Println("err是:", err)}}()num1 := 10num2 := 0result := num1 / num2fmt.Println(result)
}
自定义错误
需要调用errors包下的New函数:函数返回error类型
func test() (err error){...return errors.New("除数不能为零")
}
err := test()
if err != nil {fmt.Println("自定义错误:",err)
}
有一种情况:程序出现错误以后,后续代码就没有必要执行,想让程序中断,退出程序:
借助:builtin包下内置函数:panic
err := test()
if err != nil {fmt.Println("自定义错误:",err)panic(err)
}
//下面代码不会执行