- 发布时间
探索-GO
学习视频: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用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针