news 2026/9/12 19:30:36

golang入门到精通

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
golang入门到精通

技术架构选型

Gin + GORM + go‑redis + zap日志 + validator + JWT + MySQL8 + Redis + go‑pay/gopay

组件是什么作用
GinWeb http 框架接收 http 接口请求,路由、中间件
GORMORM 库操作 MySQL8,写数据库不用原生 SQL
go‑redisGo 客户端 SDK,github.com/redis/go-redis/v9代码层面,用来连接、读写 Redis 服务的库
zap日志库打印结构化日志,输出文件 /stdout
validator参数校验库校验 http 入参结构体
JWT鉴权组件生成 / 解析 token,登录鉴权
MySQL8数据库服务主业务存储(订单、用户)
Redis独立缓存服务(docker 部署)缓存、计数器、限流、黑名单、会话存储
// 支持支付宝、微信支付;支持**公钥模式、证书模式**,回调通知验签、退款、查询、转账等功能 go get github.com/go-pay/gopay

安装本地开发环境

1. 下载地址:All releases - The Go Programming Language

2. 查询golang版本号

go version // go version go1.27.1 windows/amd64

运行你的第一个golang程序

main.go

package main import ("fmt") func init() { // 使用 fmt 包的 Println 函数输出 "init" 字符串 fmt.Println("init") } // main 是程序的入口函数 func main() { // 使用 fmt 包的 Println 函数输出 "Hello, Services!" 字符串 fmt.Println("Hello, Services!") }

运行....

PS C:\src\go\src\bases> go build PS C:\src\go\src\bases> .\bases.exe init Hello, Services! PS C:\src\go\src\bases> go clean PS C:\src\go\src\bases> go build PS C:\src\go\src\bases> go clean PS C:\src\go\src\bases> go run .\main.go init Hello, Services!

go env用于打印Go语言的环境信息。

go run命令可以编译并运行命令源码文件。

go get可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。

go build命令用于编译我们指定的源码文件或代码包以及它们的依赖包。go install用于编译并安装指定的代码包及它们的依赖包。

go clean命令会删除掉执行其它命令时产生的一些文件和目录。

报错

PS C:\src\go\src\bases> go run .\main.go main.go:7:2: cannot find package "bases/hello" in: C:\src\go\src\bases\hello main.go:4:2: found packages hello (hello.go) and main (main.go) in C:\src\go\src\bases

报错原因分析

  1. 同一个目录下不能有不同的包:
    报错信息found packages hello (hello.go) and main (main.go) in C:\src\go\src\bases明确指出,在bases目录下,hello.go声明了包名为hello,而main.go声明了包名为main。在 Go 中,同一个目录下的所有.go文件必须属于同一个包。Go 编译器在加载包时发现冲突,直接报错。

  2. 相对路径导入和重复导入:
    你的代码中import hello "bases"_ "./hello"试图导入同一个逻辑包,且使用了相对路径./hello,这在现代 Go(Go 1.16+)中是不被推荐的,且容易引发路径解析混乱。

解决方案

hello包移到子目录中(推荐,符合 Go 工程规范)

如果你希望hello是一个独立的包供main调用,它必须拥有自己独立的目录。

基础知识

main 和 init 函数的区别

相同点:
两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。
不同点:

init可以应用于任意包中,且可以重复定义多个。
main函数只能用于main包中,且只能定义一个。

下划线的使用场景

1.不需要把整个包都导入进来,仅仅是是希望它执行init()函数。

2.占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值。

f, _ := os.Open("test.txt") defer f.Close() buf := make([]byte, 1024) for { n, _ := f.Read(buf) if n == 0 { break } os.Stdout.Write(buf[:n]) }
定义变量方式
func main() { hello.Print() var name string = "ms"// 规范命名 var age = "18"// 类型推导 sex := "man"// 短变量声明 fmt.Println(name, age, sex) }
基本类型

1. 数字:整型(byte/rune/int32/int64)、浮点型(float32/float64)

2. bool布尔值、string字符串、array数组、struct结构体、引用类型(slice/map/channel)、interface接口、function函数

注意:在 Go 语言中,不推荐使用下划线命名法(如struct_name)。官方推荐使用驼峰命名法(CamelCase),并且首字母小写表示该变量仅在当前包内可见

替换字符串
func main() { hello.Print() s1 := "ii" // 强制类型转换 byteS1 := []byte(s1) byteS1[0] = 'H' fmt.Println(string(byteS1)) s2 := "博客" // s1 := s2[1:] s4 := string([]rune(s2)[1:]) s3 := "狗狗" + s4 fmt.Println(s3) }
关于赋值
func main() { // := 用于声明并赋值,左边必须有新变量。 // = 用于单纯赋值,左边必须全是已声明的变量。 s1 := "ii" s2 := "博客" s1 = s2 fmt.Println(s1) }
结构体Struct
func main() { structName := struct { name string age int }{name: "李四", age: 20} fmt.Println(structName.name, structName.age) dd := [...]struct { name string age int }{ {"张三", 18}, {"李四", 20},// 别忘了最后一行的逗号。 } for _, v := range dd { fmt.Println(v.name, v.age) } }
数组Array
func main() { // 一维数组 // var s1 = [...]int{1, 2, 3} s1 := [...]int{1, 2, 3} for _, v := range s1 { fmt.Println(v) } // 二维数组 s2 := [...][2]int{{1, 2}, {3, 4}, {5, 6}} for _, v := range s2 { fmt.Println(v) } var struct_name = [2]struct { name string age int }{{name: "张三", age: 18}, {name: "李四", age: 20}} for key, item := range struct_name { fmt.Println(key, item.name, item.age) } }
切片Slice
  • [] T 切片;[N] T 数组
  • slice 三要素:指针、len、cap
  • 切片只是窗口,底层数组才存数据
  • 同数组切出来的切片,共享内存
  • append:满了就扩容,换新数组,返回新切片
  • copy:复制元素,不自动扩容目标
  • 传参:值拷贝 slice 头;修改原有元素外部可见;扩容后外部不可见
  • nil 切片 len cap 都是 0,可以 append
func main() { // [] T 切片;[N] T 数组 // 要理解 Go 语言的切片,最直观的方法就是把它想象成餐厅的桌子: // 长度:当前已经坐在桌子上吃饭的人数。 // 容量:这张桌子最多能坐下的总人数(包含正在吃和空着的位子)。 // s := make([]int, len, cap) // len 表示长度,cap 表示容量 s := make([]int, 5, 6) s[0] = 1 s[1] = 1 s[2] = 1 s[3] = 1 s[4] = 1 // // 如果你一开始只确定了部分长度,后续需要动态增加元素,必须使用内置函数 append,而不能直接用索引赋值。 // // 当 append 发现原来的容量不够用时,它会自动在内存中寻找一块更大的空间, // // 把原来桌子上的所有人(旧数据)复制过去,然后再把新客人加进去。这就是所谓的扩容。 s = append(s, 3, 4, 5) fmt.Println(s)// [1 1 1 1 1 3 4 5] arr := [5]int{1, 2, 3, 4, 5} s1 := arr[1:3] // [2,3] len=2 cap=4 s2 := append(s1, 99) // 同数组切出来的切片,共享内存,修改一个会影响另一个。 s3 := make([]int, 10) copy(s3, s2) //copy 用来切断共享底层数组,单独一份数据。 s3 = append(s3, 88) fmt.Println(s1, s2, s3, arr) // [2 3] [2 3 99] [2 3 99 0 0 0 0 0 0 0 88] [1 2 3 99 5] }
指针

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

  • &:取地址符。&a→ 获取变量a的内存地址
  • *:解引用符。*p→ 根据指针 p 里存的地址,取出对应内存的值
  • 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

  • new与make的区别

1.二者都是用来做内存分配的。

2.make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;

3.而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

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) b := 10 p := new(int) p = &b *p = 100 fmt.Println(*p, b) }
Map

map 是基于哈希表实现的无序键值对集合,属于引用类型。

引用类型:将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

1. 定义语法

map[KeyType]ValueType // KeyType:表示键的类型。 // ValueType:表示键对应的值的类型。 // map类型的变量默认初始值为nil,需要使用make()函数来分配内存。 make(map[KeyType]ValueType, [cap])

2. 基本使用

func main() { // 方式1 scoreMap := make(map[string]int, 8) scoreMap["张三"] = 90 scoreMap["小明"] = 80 fmt.Println(scoreMap) fmt.Println(scoreMap["小明"]) // 方式2 userInfo := map[string]string{ "username": "pprof.cn", "password": "123456", } fmt.Println(userInfo) // 判断某个键是否存在 v, ok := scoreMap["张三"] if ok { fmt.Println(v) } else { fmt.Println("查无此人") } // 遍历 for k, v := range scoreMap { fmt.Println(k, v) } // 使用delete()函数删除键值对 delete(scoreMap, "小明")//将小明:100从map中删除 // 引用类型实例说明 user := map[string]string{ "username": "admin", "password": "123456", } var user2 map[string]string user2 = user user2["age"] = "10" fmt.Println(user, user2) // map[age:10 password:123456 username:admin] map[age:10 password:123456 username:admin] }
结构体
  • Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
  • 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
  • 只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。
  • 结构体实例化的核心思路是对结构体字段进行赋值,来初始化一个结构体变量。
  • 结构体是自定义的复合数据类型,把多个不同类型的字段打包成一个整体,是 Go 实现面向对象的核心载体(Go 没有 class 类,靠 struct + 方法 + 接口实现面向对象)
  • 字段访问:通过.运算符,例如user.Name
  • 访问控制:Go 没有 public/private 关键字,靠字段名首字母大小写控制可见性,作用域为包级别

1. 定义

type 类型名 struct { 字段名 字段类型 字段名 字段类型 … } 1.类型名:标识自定义结构体的名称,在同一个包内不能重复。 2.字段名:表示结构体字段名。结构体中的字段名必须唯一。 3.字段类型:表示结构体字段的具体类型。

2. 实例化

type person struct { name string city string age int8 } func main() { // 1. 基本实例化 var p1 person p1.name = "pprof.cn" p1.city = "北京" p1.age = 18 fmt.Printf("p1=%v\n", p1) //p1={pprof.cn 北京 18} // 2. 使用键值对初始化(最推荐) p5 := person{ name: "pprof.cn", city: "北京", age: 18, } fmt.Printf("p5=%#v\n", p5) // 1. 创建指针类型结构体 var p2 = new(person) p2.name = "测试" p2.age = 18 p2.city = "北京" fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"测试", city:"北京", age:18} // 2. 取结构体的地址实例化 p3 := &person{} p3.name = "博客" p3.age = 30 p3.city = "成都" fmt.Printf("p3=%#v\n", p3) // 匿名结构体 var user struct{Name string; Age int} user.Name = "pprof.cn" user.Age = 18 fmt.Printf("%#v\n", user) }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 19:29:53

ITIL4时代运维服务转型:从工具导向到价值地图

1. ITIL4时代运维服务的范式转移十年前我刚入行运维时,服务目录就是Excel里那张永远对不上实际环境的"僵尸清单"。直到某次系统宕机,业务部门指着我们精心维护的200项服务条目质问:"这些工具堆砌和我们业务到底有什么关系&…

作者头像 李华
网站建设 2026/9/12 19:28:35

同步电机与构网型变流器频率稳定性对比研究

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 19:27:40

系统架构师的核心职责与能力模型解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 19:27:27

手机掌控AI开发:Kiro+Claude Code+Codex规约协同实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华