学习视频: B 站
安装
Go 官网下载地址:https://golang.org/dl/
Go 官方镜像站(推荐):https://golang.google.cn/dl/
安装方式
windows 可以直接下载 .exe 文件即可
linux Debian 系统 没有版本限制直接 sudo apt update && sudo apt install golang-go 一般版本比较旧,但是相对稳定,比如现在已经是 1.25.x 版本了,提供最新的下载还是 1.19.x
# 下载安装包(以 1.24.5 为例,请替换为实际版本号)wget https://go.dev/dl/go1.24.5.linux-amd64.tar.gz
# 解压到 /usr/local 目录 [citation:1][citation:5]
sudo tar -C /usr/local -xzf go1.24.5.linux-amd64.tar.gz
# 编辑当前用户的配置文件
echo 'export PATH="$PATH:/usr/local/go/bin"' >> ~/.bashrc
# 或编辑 ~/.profile[citation:1][citation:7]
# 使配置立即生效 [citation:1]
source ~/.bashrc
# 多版本管理器
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
source ~/.gvm/scripts/gvm
# 直接上面的执行会因为访问外网超时
# 解决办法:1、使用镜像 2、手动安装(将文件下载本地,然后上传服务器)# 使用下面两个其中一个镜像
bash < <(curl -s -S -L https://raw.fastgit.org/moovweb/gvm/master/binscripts/gvm-installer)
# 使用 ghproxy 代理访问
bash < <(curl -s -S -L https://ghproxy.com/https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
# 我这边是镜像都不行,只能在 windows 本地打开:https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer
# 在服务器创建 gvm-installer.sh 文件,将上面的内容复制到文件中
# 给 gvm-installer.sh 执行权限
chmod +x gvm-installer.sh
# 执行
./gvm-installer.sh
# 执行上面文件后会报缺少安装依赖,安装 gvm 依赖
sudo apt update && sudo apt install -y git curl make binutils bison gcc build-essential
# 安装 执行
source /root/.gvm/scripts/gvm
gvm help # 出现 Usage: gvm [command] 等就是安装成功了,就可以安装不同版本
## 重点,若是使用的是云服务器厂商,直接让 ai 安装,避免了镜像源等问题出现
# 安装指定版本
gvm install go1.21.5
gvm use go1.21.5 --default
自定义安装 gvm 问题修复
# GVM 修复指南
## 问题描述
GVM (Go Version Manager) 的 `gvm use` 命令报错:```
ERROR: Couldn't source environment
```
## 问题原因
GVM 的 environments 目录缺少 Go 版本对应的环境配置文件,导致 use 命令无法加载环境变量。## 修复步骤
### 1. 检查当前状态
```bash
ls -la /root/.gvm/environments/
ls -la /root/.gvm/gos/
```
### 2. 创建环境配置文件
```bash
# Go 1.21.6
cat > /root/.gvm/environments/go1.21.6 << 'ENV'
export GOROOT="$GVM_ROOT/gos/go1.21.6"
export GOPATH="$GVM_ROOT/pkgsets/go1.21.6/global"
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
ENV
# Go 1.23.0
cat > /root/.gvm/environments/go1.23.0 << 'ENV'
export GOROOT="$GVM_ROOT/gos/go1.23.0"
export GOPATH="$GVM_ROOT/pkgsets/go1.23.0/global"
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
ENV
```
### 3. 创建目录结构
```bash
mkdir -p /root/.gvm/pkgsets/go1.21.6/global
mkdir -p /root/.gvm/pkgsets/go1.23.0/global
mkdir -p /root/.gvm/gos/go1.21.6/pkgset/global
mkdir -p /root/.gvm/gos/go1.23.0/pkgset/global
```
### 4. 验证修复
```bash
export GVM_ROOT=/root/.gvm
source $GVM_ROOT/scripts/gvm-default
gvm use go1.23.0
go version
gvm use go1.21.6
go version
gvm use go1.23.0 --default
```
## 使用说明
### 基本命令
```bash
# 加载 GVM 环境(每次新终端都需要)export GVM_ROOT=/root/.gvm
source $GVM_ROOT/scripts/gvm-default
# 切换 Go 版本
gvm use go1.23.0
gvm use go1.21.6
# 设置默认版本
gvm use go1.23.0 --default
# 查看已安装版本
gvm list
# 查看当前版本
go version
```
### 自动加载配置
将以下内容添加到 `~/.bashrc` 或 `~/.zshrc`:```bash
export GVM_ROOT=/root/.gvm
source $GVM_ROOT/scripts/gvm-default
```
然后执行:```bash
source ~/.bashrc # 或 source ~/.zshrc
```
## 手动安装新版本
### 1. 下载并解压
```bash
cd /tmp
wget https://mirrors.aliyun.com/golang/go1.xx.x.linux-amd64.tar.gz
mkdir -p /root/.gvm/gos/go1.xx.x
tar -C /root/.gvm/gos/go1.xx.x --strip-components=1 -xzf go1.xx.x.linux-amd64.tar.gz
```
### 2. 创建环境文件
```bash
cat > /root/.gvm/environments/go1.xx.x << 'ENV'
export GOROOT="$GVM_ROOT/gos/go1.xx.x"
export GOPATH="$GVM_ROOT/pkgsets/go1.xx.x/global"
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
ENV
```
### 3. 创建目录结构
```bash
mkdir -p /root/.gvm/pkgsets/go1.xx.x/global
mkdir -p /root/.gvm/gos/go1.xx.x/pkgset/global
```
### 4. 验证安装
```bash
gvm use go1.xx.x
go version
```
## 常见问题
### Q1: 为什么 gvm use 报错 "Couldn't source environment"?**A:** 因为缺少对应 Go 版本的环境配置文件,需要手动创建 `/root/.gvm/environments/go 版本号 ` 文件。### Q2: 切换版本后 go version 没有变化?**A:** 需要确保:1. 已加载 GVM 环境:`source $GVM_ROOT/scripts/gvm-default`
2. 环境文件存在且配置正确
3. 重新打开终端或重新加载配置
### Q3: 如何添加新的 Go 版本?**A:** 参考上面的 "手动安装新版本" 部分,需要:1. 下载并解压到 `/root/.gvm/gos/`
2. 创建环境配置文件
3. 创建必要的目录结构
### Q4: gvm list 不显示版本?**A:** 确保 Go 版本已正确安装在 `/root/.gvm/gos/` 目录下,目录命名格式为 `go 版本号 `。### Q5: 如何完全卸载 GVM?**A:** ```bash
rm -rf /root/.gvm
# 从 ~/.bashrc 或 ~/.zshrc 中删除 GVM 相关配置
```
## 环境变量说明
- **GOROOT**: Go 安装目录
- **GOPATH**: Go 工作空间目录
- **PATH**: 包含 Go 二进制文件路径
安装对应电脑最新版本, 在 1.11 版本以后环境变量都已经集成到 go 中了
# 查看版本
go version
# 查看环境变量
go env
==>
set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=0
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=
set GOCACHE=C:\Users\admin\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\admin\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\admin\AppData\Local\Temp\go-build1200714809=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=NUL
set GOMODCACHE=C:\Users\admin\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\admin\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=D:\Go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\admin\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=D:\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.25.4
set GOWORK=
set PKG_CONFIG=pkg-config
vscode 安装 go 插件:go(搜索第一个)。在开发环境中使用提示词
设置代理
# 1. 编辑你的 Shell 配置文件(例如 ~/.bashrc, ~/.zshrc 或 ~/.profile)# 使用文本编辑器打开,例如:nano ~/.bashrc
# 2. 在文件末尾添加以下行(以 goproxy.cn 为例)export GOPROXY=https://goproxy.cn,direct
# 如果你需要同时设置多个代理,可以用逗号分隔,例如:# export GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct
# 3. 保存文件,然后让配置生效
source ~/.bashrc
Demo
// 定义当前文件的包名 随意
package main
// fmt 打印包名
import "fmt"
var a = "hello"
func main() {
// Println 输出并换行
fmt.Println("hello world")
// Print 打印输出不换行, 多个 Print 打印输出不换行, 会在同一行输出
fmt.Print("hello world A")
fmt.Print("hello world B")
// 打印中有变量,输出变量
fmt.printf("适合 a=%d a 的类型 a=%T",a,a)
}
go 语言和其他语言不同的地方是:声明的变量必须使用否则报错。
变量名称的命名:Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。Go 语言中关键字 和保留字都不能使用
- 变量名 变量类型 = 变量值 =>:var a string = “hello”、var m string、var n = 10 (支持类型推导)
- 短变量声明法(不能作为全局变量声明)=> 变量名:变量值:a : = 10
- 变量名不能在同一作用域重复声明
# 声明多个变量
var (
a = 10
c = "hello"
)
匿 名变量用一个下划线 表示,例如
匿名变量 在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。func getUserinfo()(string, int){return "zhangsan", 10}
var name,age = getUserinfo()
# => 若是声明单个变量:var user,_ = getUserinfo()
// 匿名变量不占用命名空间,不会分配内存,所以名变量之间不存在重复声明
# 常量
const (
a = 10
b = "hei"
)
//const 同时声明多个常量时,如果省略了值则表示和上面一行的值相同。const (
a = 10
b
)
# iota 是 golang 语言的常量计数器, 只能在常量的表达式中使用。# iota 在 const 关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量
# 声明将使 iota 计数一次 iota 可理解为 const 语句块中的行索
const (
n1 = iota
n2
n3
n4
)
fmt.Print(n1,n2,n3,n4) ==> 0,1,2,3
const(
n1 = iota //0
n2=100 //100
n3 = iota //2
n4 //3
)
fmt.Println(n1,n2,n3,n4)
在 Go 语言中:
- 单引号
'包围的字符字面量表示的是 rune 类型(Unicode 码点)- rune 类型实际上是 int32 的别名,它存储的是字符的 Unicode 码点数值
- 当你直接打印 rune 类型变量时,Go 默认会输出其对应的整数值(Unicode 码点)
数据类型
基本数据类型
整型
整型分为以下两个大类:
有符号整形按长度分为:int8、int16、int32、int64
对应的无符号整型:uint8、uint16、uint32、uint64
通过 unsafe.Sizeof 查看不同长度的整型 在内存里面的存储空间
数字字面量语法 %v 表示原样输出 %d 表示 10 进制输出 %b 表示二进制输出 %o 八进制输出 %x 表示 16 进制
int 不同长度直接的转换
var al int32 = 10
var a2 int64 = 21
fmt.Println(int64(a1)+ a2)// 把 a1 转换成 64 位
fmt.Println(a1 +int32(a2))// 把 a2 转换成 32 位
在电商、金融环境使用第三方包来解决浮点数精度损失问题:https://github.com/shopspring/decimal
布尔值
Go 语言中以 bool 类型进行声明布尔型数据,布尔型数据只有 true(真)和 false(假)两个值
注意:
1. 布尔类型变量的默认值为 false。
2.Go 语言中不允许将整型强制转换为布尔型.
3. 布尔型无法参与数值运算,也无法与其他类型进行转换:
字符串
| 介绍 | 方法 |
| 求长度 | len(str) |
| 拼接字符串 | + 或 fmt.Sprintf |
| 分割 | strings.Split |
| 判断是否已含 | strings.contains |
| 前缀 / 后缀判断 | strings.HasPrefix,strings.HasSuffix |
| 子串出现的位置 | strings.Index(),strings.Lastindex() |
| join 操作 | strings.Join(a[]string, sep string |
一个字母占用一个字节,一个汉字占用 3 个字节
func changeString(){
s1 := "big" // 强制类型转换
bytes1 := []byte(s1)
byteS1[0]="p"
fmt.Println(string(bytes1))
s2 :="白萝卜"
runeS2 := []rune(s2)
runeS2[0]=' 红”fmt.Printin(string(runeS2))
}
类型转换
注意:Sprintf 使用中需要注意转换的格式 int 为 %d float 为 %f bool 为 %t byte 为 %c
var i int = 20
var f float64 = 12.456
var t bool = true
var b byte = 'a'
str1 := fmt.Sprintf("%d",i)
fmt.Printf("值:%v 类型:%T\n",str1,str1)=> 值:20 类型:string
str2 := fmt.Sprintf("%f",f)
fmt.Printf("值:%v 类型:%T\n",str2,str2)=> 值:20 类型:string
str3 := fmt.Sprintf("%t",t)
fmt.Printf("值:%v 类型:%T\n",str3,str3)=> 值:true 类型:string
str4 := fmt.Sprintf("%c",b)
fmt.Printf("值:%v 类型:%T\n",str4,str4)=> 值:a 类型:string
注意: 在 go 语言中数值类型没法直接转换成 bool 类型 bool 类型也没法直接转换成数值类
运算符
1、除法注意: 如果运算的数都是整数,那么除后,去掉小数部分,保留整数部分
2、取余注意 余数 = 被除数 -(被除数 / 除数)* 除数
3、注意: ++(自增)和 –(自减)在 Go 语言中是单独的语句,并不是运算符。
var a= 10
var b=3
fmt.Println(a%b) //1
fmt.Println(-10%3) //-10-(-10/3)*3 =-1
fmt.Println(10%-3) // 10-(10/-3)*-3=1
遍历
goto 语句通过标签进行代码间的无条件跳转。goto 语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go 语言中使用 goto 语句能简化一些代码的实现过程。
var n = 30
if n>=20{goto label66}
fmt.Println("n<20")
label66:
fmt.Println("hello world")
引用类型
数组 | 切片
// arr := strings.split(str1,"-")
// str2 := strings.Join(arr,"*")
// fmt.Println(str2)//123*456*789
arr := []string{"php","java","golang"}
// fmt.Println(arr)
str3 := strings.Join(arr,"-")
// fmt.Println(str3)
fmt.Printf("%v-%",str3,str3)
var a = [5]int // 初始化数组 长度为 5 的数字
b :=[3]string{"css","javascript","golang"}
var b = [...]int{1,22,3335,8787,4545} // 自动推导长度
c = [...]int{0:1,1:3,5:6} // 长度为 6 [1 3 0 0 0 6]
// 数组的值类型和引用类型示例
var arr1 = [...]int{1,2,3}
arr2 := arr1
arr1[0] = 10
fmt.Println(arr1)//[10 2 3]
fmt.Println(arr2)//[1 2 3]
var arr3 = []int{1,2,3}
arr4 := arr3
arr3[0] = 11
fmt.Println(arr3)//[11 2 3]
fmt.Println(arr4)// [11 2 3]
// 值类型: 改变变量副本值的时候,不会改变变量本身的值
// 引用类型: 改变变量副本值的时候,会改变变量本身的值
var arr1 []int
var arr2 = []int{1,2,34,45}
fmt.Println(arr1)//[]
fmt.Println(arr1 == nil)//true golnag 中申明切片以后 切片的默认值就是 ni1
fmt.Println(arr2 == nil)//false
在 go 中数组和切片定义是非常像的,但是切片不限定长度,数组在定义的时候已经定义了长度,不可以扩容
由于切片的底层就是一个数组,所以我们可以基于数组定义切片。
关于切片的长度和容量:
- 长度: 切片的长度就是它所包含的元素个数
- 容量: 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
- 没法通过下标的方式给切片扩容:给切片扩容的话要用到 append()方法
a:=[5]int{55,56,57,58,59}
b := a[:] // 获取数组里面的所有值
fmt.Printf("%v-%T\n",b,b)//[55 56 57 58 59]-[]int
c := a[1:4]
fmt.Printf("%v-%T\n",c,c)//[56 57 58]-[]int
d := a[2:]// 表示获取第二个下标后面的数据
fmt.Printf("%v-%T\n",d,d)//[57,58,59]-[]int
e := a[:3] // 表示获取第三个下标前面的数据
fmt.Printf("%v-%T\n",e,e)//[55,56,57]-[]int
s:=[]int{2,3,5,7,11,13}
fmt.Printf("长度 %d 容量 %d\n",len(s),cap(s))// 长度 6 容量 6
a := s[2:]//5,7,11,13
fmt.Printf("长度 %d 容量 %d\n",len(a),cap(a))// 长度 4 容量 4
b := s[1:3]//3,5
fmt.Printf("长度 %d 容量 %d\n",len(b),cap(b))// 长度 2 容量 5
// make 创建切片
var sliceA = make([]int,4,8)
// fmt.Println(sliceA)//[0 8 8 0]
sliceA[0]= 10
sliceA[1] = 12
sliceA[2]= 40
sliceA[3]= 30
fmt.Println(sliceA)// [10 12 40 30]
var sliceA []int
sliceA = append(sliceA,12,23,35,465)
fmt.Printf("%v-%v--%v",sliceA,len(sliceA),cap(sliceA))// [12 23 35 465]-4--4
sliceA:=[]string{"php","java"}
sliceB:=[]string{"nodejs","python"}
sliceA = append(sliceA, sliceB...)
fmt.Println(sliceA)//[php java nodejs python]
append 合并切片的时候最后一个元素要加…
Map 类型
make 创建 map 类型的数据
var userinfo = make(map[string]string)
var userinfo = map[string]string{"username":"张三”,"age":"20","sex":" 男”}
userinfo := map[string]string{"username":"张三”,"age":"20","sex":" 男”}
Map 切片
var userinfo = make([]map[string]string, 3,3)
// fmt.Println(userinfo[e])//map[]map 不初始化的默认值 ni1
fmt.Println(userinfo[0] == nil)//true
函数
函数的可变参数,可变参数是指函数的参数数量不固定。Go 语言中的可变参数通过在参数名后加 … 来标
func sum(x,y int) int {
sub := x+y
return sub
}
func sumFn(x ...int)int {
sum := 0
for _, v := range x{sum += V}
return sum
}
//return 关键词一次可以返回多个值
func calc(x,y int)(int, int){
sum :=X+y
sub :=x-y
return sum, sub
}
func main(){a,b:= calc(10,2)
fmt.Println(a, b)
}
// 命名返回值
func calc1(x,y int)(sum int, sub int){fmt.Println(sum, sub)
sum=x+y
sub =x-y
fmt.Println(sum, sub)
return
}
func calc2(x,y int)(sum, sub int){
sum=x+y
sub =x-y
return
}
函数变量作用域:
- 全局变量: 全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效(全局作用域)
- 局部变量: 局部变量是函数内部定义的变量,函数内定义的变量无法在该函数外使用(局部作用域)
函数可以作为一个类型,参数;但是在一个函数 A 内不能声明另一个函数,只能在这个函数 A 外定义,然后在 A 内使用,除了匿名自执行函数(就是定义的匿名函数立即执行)
type calc func(int,int)int // 表示定义一个 calc 的类型
func add(x,y int) int{return x+y}
func sub(x,y int) int{return x-y}
# 参数是一个函数
func suc(x,y int, c calc) int{return calc(x,y)
}
func main(){
var c calc
c= sub // 符合同一类型
h := suc(6,2,add)
# 匿名自执行函数
func (x,y int) {fmt.PrintIn("test....")
}(10,20)
}
defer 延迟执行语句
有点像栈,先 defer 后输出
func a(x int){defer func(x int){
x++
return
}
return x
}
func main (){fmt.PrinIn("开始")
defer fmt.PrinIn("1")
defer fmt.PrinIn("2")
defer fmt.PrinIn("3")
fmt.PrinIn("结束")
// ==> 开始 结束 3 2 1
a(4)
}
提示:defer 注册要延迟执行的函数时该函数所有的参数都需要确定其值
func main(){
var a int = 1
var b int = 2
defer fmt.Println("a", a, "b", b)
a = 10
defer fmt.Println("A=", a, "B=", b)
b = 20
// A= 10 B= 2
// a 1 b 2
}
panic | recover
在 Go 中没有 try{}catch(err){}方式的函数,panic 只有主动抛出错误,和在抛出错误之前使用 recover 拦截错误
import (
"errors"
"fmt"
)
func readFile(fileName string) error {
if fileName=="main.go"{return nil} else {return errors.New("读取文件失败")
}
}
func myFn(){defer func(){err := recover()
if err != nil {fmt.Println("给管理员发送邮件")
}
}()
err := readFile("xxx.go")
if err != nil {panic(err)
}
}
func main(){myFn()
// 给管理员发送邮件
}
time 和 date 包
func main() {timeObj := time.Now()
// 格式时间
fmt.Println(timeObj.Format("2006-01-02 15:04:05"))
fmt.Println(timeObj.Format("2006/01/02 15:04:05"))
fmt.Println(timeObj.Format("2006/01/02 15:04:05"))
year := timeObj.Year()
month := timeObj.Month()
day := timeObj.Day()
hour := timeObj.Hour()
minute := timeObj.Minute()
second := timeObj.Second()
fmt.Printf("%d-%d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
unixtime := timeobj.Unix()// 获取当前的时间戳
unixTime := 1587894706
timeobj := time.Unix(int64(unixTime),0)
var str =timeobj.Format("2006-01-02 15:04:05")
fmt.Println(str)
// 定时器
ticker := time.NewTicker(time.Second)
n := 5
for t := range ticker.C {
n--
fmt.Println(t)
if n == 0 {ticker.Stop() // 终止这个定时器继续执行
break
}
}
}
new 和 make
都是用来做内存分配,两者区别是在于 make 用于 slice、map、channel 的初始化,new 用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针