Gin
pkg.go.dev
go.mod是模块依赖,每创建一个 Go Mod 项目都会生成一个go.mod文件。
go.sum文件在创建项目之初并不会存在,只有在真正引用了外部依赖后,才会生成该文件,go.sum文件并不适合人类阅读,也不建议手动修改该文件。它的作用主要是解决一致性构建问题,即不同的人在不同的环境中使用同一个的项目构建时所引用的依赖包必须是完全相同的,这单单靠一个go.mod文件是无法保证的。
数据类型
Go 是一个典型的静态强类型语言,静态指所有变量的类型都会在编译期确定好,并且在整个程序的生命周期都不会再改变。
强类型指在程序中执行严格的类型检查,如果出现类型不匹配则会报错。
Go 的声明方式始终遵循名字在前面,类型在后的原则。可以通过type关键字声明类型,声明的类型都是新类型,不同的类型无法进行运算,即便基础类型是相同的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 声明新类型
type MyFloat64 float64
var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(f1 + f)
invalid operation: f1 + f (mismatched types MyFloat64 and float64)
// 为int类型起一个别名,可以进行运算
type Int = int
var a Int = 1
var b int = 2
fmt.Println(a + b)
|
类型断言:
1
2
3
4
5
6
7
8
9
|
var b int = 1
var a interface{} = b
// a.(int)指相信a是int类型,然后取其值。第一个返回值是目标类型(intVal),第二个返回值是bool(ok)。
// ok是一个普通变量名
if intVal, ok := a.(int); ok {
fmt.Println(intVal)
} else {
fmt.Println("error type")
}
|
类型推断
1
2
3
4
5
6
|
var a interface{} = 2
switch a.(type) {
case int: fmt.Println("int")
case float64: fmt.Println("float")
case string: fmt.Println("string")
}
|
内置类型any就是interface{}的类型别名,两者完全等价,仅仅叫法不一样。
布尔类型
bool true为真值,false为假值。整数 0 并不代表假值,非零整数也不能代表真值,即数字无法代替布尔值进行逻辑判断,两者是完全不同的类型。
无符号整型与有符号整型
1
2
3
4
5
6
7
8
9
10
11
|
uint8 无符号 8 位整型
uint16 无符号 16 位整型
uint32 无符号 32 位整型
uint64 无符号 64 位整型
int8 有符号 8 位整型
int16 有符号 16 位整型
int32 有符号 32 位整型
int64 有符号 64 位整型
uint 无符号整型 至少 32 位
int 整型 至少 32 位
uintptr 等价于无符号 64 位整型,但是专用于存放指针运算,用于存放死的指针地址。
|
指针
指针被定义后没有分配到任何变量时,它的值为 nil,称为空指针。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
//指针变量的存储地址
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
//使用指针访问值
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
|
浮点型
IEEE-754浮点数
float32 IEEE-754 32 位浮点数
float64 IEEE-754 64 位浮点数
复数类型
complex128 64 位实数和虚数
complex64 32 位实数和虚数
字符类型
go 语言字符串完全兼容 UTF-8
-
byte 等价 uint8 可以表达 ANSCII 字符
-
rune 等价 int32 可以表达 Unicode 字符
-
string 字符串即字节序列,可以转换为[]byte类型即字节切片
派生类型
1
2
3
4
5
6
7
8
|
数组 [5]int,长度为 5 的整型数组
切片 []float64,64 位浮点数切片
映射表 map[string]int,键为字符串类型,值为整型的映射表
结构体 type Gopher struct{},Gopher 结构体
指针 *int,一个整型指针。
函数 type f func(),一个没有参数,没有返回值的函数类型
接口 type Gopher interface{},Gopher 接口
通道 chan int,整型通道
|
零值
zero value,零值并不仅仅只是字面上的数字零,而是一个类型的空值或默认值。
nil类似于其它语言中的none或者null,但并不等同。nil仅仅只是一些引用类型的零值,不属于任何类型,仅只是一个变量。
1
2
3
4
5
6
|
数字类型 0
布尔类型 false
字符串类型 ""
数组 固定长度的对应类型的零值集合
结构体 内部字段都是零值
切片,映射表,函数,接口,通道,指针 nil
|
常量
常量用const声明,批量声明常量可以用()括起来以提升可读性。在同一个常量分组中,在已经赋值的常量后面的常量可以不用赋值,其值默认就是前一个的值,比如:
1
2
3
4
5
6
7
|
const (
A = 1
B // 1
C // 1
D // 1
E // 1
)
|
iota是一个内置的常量标识符,通常用于表示一个常量声明中的无类型整数序数,一般都是在括号中使用。
1
2
3
4
5
6
7
8
9
|
const iota = 0
const (
Num = iota // 0
Num1 // 1
Num2 // 2
Num3 // 3
Num4 // 4
)
|
iota是递增的,第一个常量使用iota值的表达式,根据序号值的变化会自动的赋值给后续的常量,直到用新的const重置,这个序号其实就是代码的相对行号,是相对于当前分组的起始行号。
1
2
3
4
5
6
7
8
9
|
const (
Num = iota<<2*3 + 1 // 1 第一行
Num2 = iota<<2*3 + 1 // 13 第二行
_ // 25 第三行
Num3 //37 第四行
Num4 = iota // 4 第五行
_ // 5 第六行
Num5 // 6 第七行
)
|
枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//自定义枚举类型 Season,底层类型是 uint8
type Season uint8
const (
// iota 是 Go 的常量计数器,在 const 块中从 0 开始自动递增
Spring Season = iota //0
Summer //1
Autumn //2
Winter //3
)
// 因为Season是自定义类型,可以通过强制类型转换将其他数字也转换成该类型
Season(6)
|
变量
变量用var声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 当要声明多个相同类型的变量时,可以只写一次类型
var numA, numB, numC int
var name string
var age int
name, age = "jack", 1
// 编译器自行推断
name := "jack" // 字符串类型的变量
// 短变量声明可以批量初始化
name, age := "jack", 1
// 多个变量交换数值
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1
a, b, c := 0, 1, 1
a, b, c = b, c, a+b 1 1 1
|
:= 的语义是:声明 + 赋值。
1
2
3
4
5
6
7
8
|
// 错误示例,a已经声明为int,默认值是0
var a int
// 再对a声明并赋值,报错!
a := 1
// 特例,在赋值旧变量的同时声明一个新的变量,这样是ok的,a被重新赋值
a := 1
a, b := 2, 2
|
go 语言中所有函数中的变量都必须被使用,否则在编译时就会报错:
a declared and not used
对于函数外的包级变量则没有这个限制!
用下划线可以表示不需要某一个变量:
1
2
3
4
|
// os.Open函数有两个返回值
Open(name string) (*File, error)
// 只想要第一个,不想要第二个
file, _ := os.Open("readme.txt")
|
go没有隐式类型转换,变量之间类型必须相同。
1
2
3
4
5
6
|
func main() {
var a uint64
var b int64
a != b
int64(a) == b
}
|
可见性
-
名称大写字母开头,即为公有类型/变量/常量
-
名字小写或下划线开头,即为私有类型/变量/常量
1
2
3
4
5
6
7
|
package example
// 公有
const MyName = "jack"
// 私有
const mySalary = 20_000
|
导包起别名,别名为下划线_时就是匿名导入,匿名导入的包无法被使用,这么做通常是为了加载包下的init 函数,但又不需要用到包中的类型,一个常见的例子就是注册数据库驱动,但是你并不需要去手动使用驱动。
1
2
3
4
5
6
7
8
9
10
|
package main
import (
e "example"
e1 "example1"
// 匿名导入
_ "mysql-driver-go"
// 将该包中的所有类型都导入到当前包作用域,以这种方法导入的类型不再需要.运算符去访问,但是如果有重名的类型将会无法通过编译。
. "example"
)
|
运算符
当两个数字使用^时,例如a^b,它就是异或运算符,只对一个数字使用时,例如^a,那么它就是取反运算符。
Go 语言中没有自增与自减运算符,它们被降级为了语句statement,并且规定了只能位于操作数的后方:a++正确,++a 错误。它们不具有返回值,因此a = b++这类语句的写法是错误的。
整型字面量
1
2
3
4
5
6
7
8
9
10
|
24 // 24
024 // 24
2_4 // 24
0_2_4 // 24
10_000 // 10k
100_000 // 100k
0O24 // 20
0b00 // 0
0x00 // 0
0x0_0 // 0
|
浮点数字面量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5. // == 15.0
0.15e+0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp+0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (integer subtraction)
|
复数字面量
1
2
3
4
5
6
7
8
9
10
11
12
|
0i
0123i // == 123i
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
|
字符字面量
1
2
3
4
5
6
7
8
9
10
11
|
'a'
'ä'
'你'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
|
转义字符
1
2
3
4
5
6
7
8
9
10
|
\a U+0007 响铃符号
\b U+0008 回退符号
\f U+000C 换页符号
\n U+000A 换行符号
\r U+000D 回车符号
\t U+0009 横向制表符号
\v U+000B 纵向制表符号
\\ U+005C 反斜杠转义
\' U+0027 单引号转义 (该转义仅在字符内有效)
\" U+0022 双引号转义 (该转义仅在字符串内有效)
|
字符串字面量
字符串字面量必须使用双引号"“括起来或者反引号``
1
2
3
4
5
6
7
8
9
10
|
`abc` // "abc"
`\n
\n` // "\\n\n\\n"
"\n"
"\"" // `"`
"Hello, world!\n"
"今天天气不错"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
|
函数
函数声明方式通过func关键字,参数类型后置,允许多返回值,而且可以带名字。
1
2
3
|
func Pos(name string) (x, y float64) {
...
}
|
格式化工具gofmt
gofmt 是 Go 语言官方提供的代码格式化工具,它的作用是自动统一 Go 代码的缩进、空格、换行等风格,确保所有 Go 项目遵循一致的编码规范。
1
2
3
4
5
6
7
8
|
# 查看格式化后的代码(不修改原文件)
gofmt your_file.go
# 直接修改原文件(最常用)
gofmt -w your_file.go
# 格式化整个目录(递归)
gofmt -w .
|
1
2
3
4
5
6
|
| 选项 | 作用 |
|------|------|
| `-w` | 将格式化结果写回原文件 |
| `-s` | 简化代码(如 `[]int{1,2,3}` → `[...]int{1,2,3}` 如果可能) |
| `-l` | 只列出需要格式化的文件名(不输出内容) |
| `-d` | 显示 diff(对比原文件与格式化后差异) |
|
Go 强制花函数后的括号不换行。
1
2
3
4
5
6
7
8
9
10
|
// 错误示例
func main()
{
fmt.Println("Hello 世界!")
}
// 正确示例
func main() {
fmt.Println("Hello 世界!")
}
|
Go 中没有三元表达式。
defer
defer 用于延迟执行函数或语句,无论函数是正常 return,还是发生 panic,defer 都会执行(除非程序直接退出),常用于资源清理(如关闭文件、解锁、关闭连接)
1
2
3
4
5
6
7
8
9
10
|
func main() {
defer fmt.Println("world") // 被推迟
fmt.Println("hello")
}
/**
输出
hello
world
/
|