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函数分别返回复数的实部和虚部:

go 复制代码
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的声明语句还可以简化:

go 复制代码
x := 1 + 2i
y := 3 + 4i

默认值

在Golang中数据类型都有一个默认值,当程序员没有赋值时,就会保留默认值(默认值又叫零值)

数据类型 默认值
整数类型 0
浮点类型 0
布尔类 false
字符串类型 ""

类型转换

显示转换

Go在不同类型的变量之间赋值时需要显式转换,并且只有显式转换(强制转换)。

语法:

表达式T(v)将值v转换为类型T

T : 就是数据类型

v : 就是需要转换的变量

基本数据类型转换为string

方式1:fmt.Sprintf("%参数",表达式) ---》 重点练习这个,推荐方式

go 复制代码
package main
import "fmt"
func main(){
        var n1 int = 19
        var n2 float32 = 4.78
        var n3 bool = false
        var 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包的函数

go 复制代码
package main
import(
        "fmt"
        "strconv"
)
func main(){
        var n1 int = 18
        var s1 string = strconv.FormatInt(int64(n1),10)   //参数:第一个参数必须转为int64类型 ,第二个参数指定字面值的进制形式为十进制
        fmt.Printf("s1对应的类型是:%T ,s1 = %q \n",s1, s1)
        var n2 float64 = 4.29
        var 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 = true
        var s3 string = strconv.FormatBool(n3)
        fmt.Printf("s3对应的类型是:%T ,s3 = %q \n",s3, s3)
}
string转换为基本数据类型

使用strconv包的函数

strconv.ParseT

2.复合数据类型

数组

go 复制代码
var 数组名 [数组大小]数据类型

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。

数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len()函数将返回数组中元素的个数。

arr中存的是地址值,和arr[0]arr[0][0]地址相同

数组的初始化
go 复制代码
//第一种:
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)

注意:

  1. 长度属于类型的一部分
  2. Go中数组属值类型,在默认情况下是值传递,因此会进行值拷贝
  3. 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
二维数组的遍历
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:定义一个切片,然后让切片去引用一个已经创建好的数组。

go 复制代码
sl := arr[2:4]

方式2:通过make内置函数来创建切片。基本语法: var 切片名 = make([]T, len,[cap]

go 复制代码
sl2 := make([]int, 2, 3)

方式3:定一个切片,直接就指定具体数组,使用原理类似make的方式。\

go 复制代码
sl3 := []int{1, 2, 3}
append函数

切片可以动态增长

go 复制代码
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,3
        fmt.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函数将切片追加给切片

go 复制代码
slice3 := []int{99,44}
slice = append(slice,slice3...)
fmt.Println(slice)
切片的拷贝
go 复制代码
//定义切片:
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

go 复制代码
var map变量名 map[keytype]valuetype
创建方式
go 复制代码
//方式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可以重复的

go 复制代码
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函数

结构体

结构体定义
go 复制代码
//定义老师结构体,将老师中的各个属性  统一放入结构体中管理:
type Teacher struct{
        //变量名字大写外界可以访问这个属性
        Name string
        Age int
        School string
}
实例创建方式

1.直接创建

go 复制代码
//创建老师结构体的实例、对象、变量:
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.创建时赋值

go 复制代码
var t Teacher = Teacher{"赵珊珊",31,"黑龙江大学"}
fmt.Println(t)

3.返回的是结构体指针

go 复制代码
var t *Teacher = new(Teacher)
(*t).Name = "马士兵"
(*t).Age = 45
//为了符合程序员的编程习惯,go提供了简化的赋值方式
t.School = "清华大学"
//go编译器底层对t.School转化(*t).School = "清华大学"
fmt.Println(*t)

4.返回的是结构体指针,创建时赋值

go 复制代码
var t *Teacher = &Teacher{"马士兵",45,"清华大学"}
fmt.Println(t)
结构体之间的转换

结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)

go 复制代码
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认为是新的数据类型,但是相互间可以强转

go 复制代码
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
go 复制代码
switch 表达式 {
	case 值1,值2,.....:
		语句块1
	case 值3,值4,...:
		语句块2
	....
	default:
		语句块
}

无条件 switch:

无条件的 switch 同 switch true 一样。

注意:

  1. switch后是一个表达式(即:常量值、变量、一个有返回值的函数等都可以)
  2. case后面的值如果是常量值(字面量),则要求不能重复
  3. case后的各个值的数据类型,必须和 switch 的表达式数据类型一致
  4. case后面可以带多个值,使用逗号间隔。比如 case 值1,值2...
  5. case后面不需要带break
  6. default语句不是必须的,位置也是随意的。
  7. switch穿透,利用fallthrough关键字,如果在case语句块后增加fallthrough ,则会继续执行下一个case

循环结构

for

Go 的 for 语句后面的三个构成部分外没有小括号, 大括号 { } 则是必须的。

for 是 Go 中的「while」 分号可以去掉,和C中的while类似

如果省略循环条件,该循环就不会结束,因此无限循环可以写得很紧凑。

for range
go 复制代码
for key, val := range coll {
    ...
}

关键字

break

结束最近的循环

continue

结束本次循环,继续下一次循环

goto

Golang的 goto 语句可以无条件地转移到程序中指定的行。

goto语句通常与条件语句配合使用。可用来实现条件转移.

在Go程序设计中一般不建议使用goto语句,以免造成程序流程的混乱。-

return

结束当前的函数

4.函数

定义:

go 复制代码
func function_name( [parameter list] ) [return_types] {
   函数体
}
闭包

Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

匿名函数是一种没有函数名的函数,通常用于在函数内部定义函数,或者作为函数参数进行传递。

以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:

go 复制代码
package main

import "fmt"

func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
     return 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 main

import "fmt"

func main() {
	test()
	fmt.Println("上面的除法操作执行成功")
	fmt.Println("正常执行下面的逻辑")
}
func test() {
	//利用defer+recover来捕获错误:defer后加上匿名函数的调用
	defer func() {
		//调用recover内置函数,可以捕获错误:
		err := recover()
		//如果没有捕获错误,返回值为零值:nil
		if err != nil {
			fmt.Println("错误已捕获")
			fmt.Println("err是:", err)
		}
	}()
	num1 := 10
	num2 := 0
	result := num1 / num2
	fmt.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)
}
//下面代码不会执行
相关推荐
齐 飞2 小时前
MongoDB笔记02-MongoDB基本常用命令
前端·数据库·笔记·后端·mongodb
flying robot2 小时前
Go结构体(struct)
笔记
王俊山IT2 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(1)
开发语言·c++·笔记·学习
千年死缓2 小时前
gin中间件
中间件·gin
xing25162 小时前
张氏宗谱序言白话文翻译
笔记
gma9993 小时前
【Effective C++】阅读笔记3
c++·笔记
安冬的码畜日常4 小时前
【The Art of Unit Testing 3_自学笔记06】3.4 + 3.5 单元测试核心技能之:函数式注入与模块化注入的解决方案简介
笔记·学习·单元测试·jest
快乐点吧5 小时前
BERT 模型在句子分类任务中的作用分析笔记
笔记·分类·bert
逻辑与&&5 小时前
[Prometheus学习笔记]从架构到案例,一站式教程
笔记·学习·prometheus