golang入门到精通

技术架构选型

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

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

安装本地开发环境

  1. 下载地址:All releases - The Go Programming Language
  1. 查询golang版本号
Go 复制代码
go version
// go version go1.27.1 windows/amd64

运行你的第一个golang程序

main.go

Go 复制代码
package main

import ("fmt")

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

运行....

bash 复制代码
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命令会删除掉执行其它命令时产生的一些文件和目录。

报错

bash 复制代码
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.*占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值。

Go 复制代码
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])
}
定义变量方式
Go 复制代码
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),并且首字母小写表示该变量仅在当前包内可见

替换字符串
Go 复制代码
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)
}
关于赋值
Go 复制代码
func main() {
	// := 用于声明并赋值,左边必须有新变量。
	// = 用于单纯赋值,左边必须全是已声明的变量。
	s1 := "ii"
	s2 := "博客"
	s1 = s2
	fmt.Println(s1)
}
结构体Struct
Go 复制代码
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
Go 复制代码
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
Go 复制代码
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 里存的地址,取出对应内存的值
Go 复制代码
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)
}
相关推荐
Leo.yuan1 小时前
Flink + Kafka + Doris 之外的另一条路:FineDataLink 一体化实时数仓方案
开发语言·javascript·ecmascript
只睡四小时1 小时前
端侧推理实战:face-api.js 零后端人脸识别踩坑记
开发语言·javascript·ecmascript
某不知名網友1 小时前
C++ 深浅拷贝:从默认拷贝到 Rule of Five
java·开发语言
君顾11 小时前
外卖CPS小程序开发实战指南:从零到上线的完整流程
java·开发语言·外卖
明月_清风1 小时前
Foundry Cheatcode 详解:prank、warp、deal、expectRevert、expectEmit
后端·web3·solidity
blue_ice .1 小时前
DDS原理及简易实现
开发语言·经验分享·笔记·嵌入式硬件·fpga开发
励志不掉头发的内向程序员1 小时前
【LibreCAD 2D架构】鼠标点下的坐标为什么会被“吸”走?LibreCAD 对象捕捉系统解析
开发语言·c++·qt·学习·系统架构
鹅城剑仙1 小时前
Spring Boot 3 全局异常处理与统一响应封装进阶实战
java·spring boot·后端
Generalzy1 小时前
像 gofmt 一样格式化 Python:Black、Ruff、YAPF、autopep8 谁才是 2026 年的首选?
开发语言·python