一、安装下载
1、go环境
下载地址:https://go.dev/dl/
或者:https://golang.google.cn/dl/
windows直接下载msi文件安装即可。
linux下载之后,进行解压+环境变量设置:
# 下载
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
# 解压
tar -zxvf go1.22.5.linux-amd64.tar.gz
# 进入到go的解压路径
/root/go/go/bin
# 临时环境变量,关闭控制台就失效了
export PATH=$PATH:/root/go/go/bin
# 编辑 ~/.bash_profile 或者 /etc/profile,添加
export PATH=$PATH:/root/go/go/bin
# 环境变量生效
source ~/.bash_profile
# 或
source /etc/profile
# 验证
[root@localhost ~]# go version
go version go1.22.5 linux/amd64
2、ide
GoLand:https://www.jetbrains.com/go/
二、基本使用
1、运行
# 直接运行
go run hello.go
# 编译为可执行文件
go build hello.go
2、结构体与方法函数指针
package main
import "fmt"
type Books struct {
/*首字母大写相当于 public。
首字母小写相当于 private。
这个 public 和 private 是相对于包(go 文件首行的 package 后面跟的包名)来说的。
当要将结构体对象转换为 JSON 时,对象中的属性首字母必须是大写,才能正常转换为 JSON。
*/
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.4399.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.4399.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.4399.com"})
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.4399.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.4399.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(Book1)
/* 打印 Book2 信息 */
printBook(Book2)
}
func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}
package main
import (
"fmt"
)
/* 定义结构体 */
type Circle struct {
radius float64
}
func main() {
var c Circle
fmt.Println(c.radius)
c.radius = 10.00
// 方法返回值用参数接收
area := c.getArea()
fmt.Println()
c.changeRadius(20)
fmt.Println(c.radius)
change(&c, 30)
fmt.Println(c.radius)
}
func (c Circle) getArea() float64 {
return c.radius * c.radius
}
// 注意如果想要更改成功c的值,这里需要传指针
func (c *Circle) changeRadius(radius float64) {
c.radius = radius
}
// 以下操作将不生效
//func (c Circle) changeRadius(radius float64) {
// c.radius = radius
//}
// 引用类型要想改变值需要传指针
func change(c *Circle, radius float64) {
c.radius = radius
}
// 这种是把【Circle类】作 为参数传递,并返回Circle类对象
func getArea2(c Circle) Circle {
var temp Circle
temp.radius = c.radius * 12
return temp
}
// 这种是返回Circle类对象
func getArea3() Circle {
var temp Circle
temp.radius = 0.999
return temp
}
3、闭包
package main
import "fmt"
func getSequence() func() int {
i := 0
return func() int {
i += 1
return i
}
}
func main() {
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber()) //这个执行结果是1
fmt.Println(nextNumber()) //这个执行结果是2
fmt.Println(nextNumber()) //这个执行结果是3
/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber1 := getSequence() //当getSequence()被重新赋值之后,nextNumber的值应该销毁丢失的,但并没有
fmt.Println(nextNumber1()) //这儿因为是新赋值的,所以是1
fmt.Println(nextNumber()) //这一行代码是补充上例子的。这儿可不是新赋的值,重点说明这一个,这儿执行居然是4,这个值并没有被销毁,原因就是闭包导致的,尽管外面的函数销毁了,但是内部函数仍然存在,还可以继续走。这个就是闭包
fmt.Println(nextNumber1()) //新赋值的,继续执行是2
}
4、指针
指针变量只能指向一个地址。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a ) // a 变量的地址是: 20818a220
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) // ip 变量储存的指针地址: 20818a220
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip ) // *ip 变量的值: 20
}
多级指针本质上就是一个指针链。
package main
import (
"fmt"
)
func main() {
var a int = 5
// 指针
var p1 *int = &a
// 二级指针
var p2 **int = &p1
// 三级指针
var p3 ***int = &p2
fmt.Printf("p1的值:%d p1的目标值:%d\n", p1, *p1) // p1的值:824633761992 p1的目标值:5
fmt.Printf("p2的值:%d p2的目标值:%d p2的链尾目标值:%d\n", p2, *p2, **p2) // p2的值:824634196008 p2的目标值:824633761992 p2的链尾目标值:5
fmt.Printf("p3的值:%d p3的目标值:%d 下一个目标值:%d p3的链尾目标值:%d\n", p3, *p3, **p3, ***p3) // p3的值:824634196016 p3的目标值:824634196008 下一个目标值:824633761992 p3的链尾目标值:5
}
向函数传递指针:
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前 a 的值 : %d\n", a )
fmt.Printf("交换前 b 的值 : %d\n", b )
/* 调用函数用于交换值
* &a 指向 a 变量的地址
* &b 指向 b 变量的地址
*/
swap(&a, &b);
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 将 y 赋值给 x */
*y = temp /* 将 temp 赋值给 y */
// *x, *y = *y, *x 可以优化为这个
}
5、map
package main
import "fmt"
/*
使用 make 函数 其中 KeyType 是键的类型,ValueType 是值的类型,initialCapacity 是可选的参数,用于指定 Map 的初始容量。
map_variable := make(map[KeyType]ValueType, initialCapacity)
*/
func main() {
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
// 修改键值对
m["apple"] = 5
// 获取 Map 的长度
len := len(m)
// 遍历 Map
for k, v := range m {
fmt.Printf("key=%s, value=%d\n", k, v)
}
// 删除键值对
delete(m, "banana")
}
6、接口
package main
import "fmt"
type Shape interface {
area() float64
}
type Rectangle struct {
width float64
height float64
}
func (r Rectangle) area() float64 {
return r.width * r.height
}
type Circle struct {
radius float64
}
func (c Circle) area() float64 {
return 3.14 * c.radius * c.radius
}
func main() {
var s Shape
s = Rectangle{width: 10, height: 5}
fmt.Printf("矩形面积: %f\n", s.area())
s = Circle{radius: 3}
fmt.Printf("圆形面积: %f\n", s.area())
}
7、异常
error 类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
示例:
package main
import (
"fmt"
)
// 自定义错误信息结构
type DIV_ERR struct {
etype int // 错误类型
v1 int // 记录下出错时的除数、被除数
v2 int
}
// 实现接口方法 error.Error()
func (div_err DIV_ERR) Error() string {
if 0==div_err.etype {
return "除零错误"
}else{
return "其他未知错误"
}
}
// 除法
func div(a int, b int) (int,*DIV_ERR) {
if b == 0 {
// 返回错误信息
return 0,&DIV_ERR{0,a,b}
} else {
// 返回正确的商
return a / b, nil
}
}
func main() {
// 正确调用
v,r :=div(100,2)
if nil!=r{
fmt.Println("(1)fail:",r)
}else{
fmt.Println("(1)succeed:",v)
}
// 错误调用
v,r =div(100,0)
if nil!=r{
fmt.Println("(2)fail:",r)
}else{
fmt.Println("(2)succeed:",v)
}
}
三、包管理
1、go mod语法
# 新建项目
mkdir myapp && cd myapp
# 初始化项目,生成go.mod
go mod init myapp
# 指定国内包源地址
go env -w GOPROXY=https://goproxy.cn
# 下载echo包
go get github.com/labstack/echo/v4
# 更新依赖
go get -u xxx
# 删除项目中没有用到的依赖包
go mod tidy
# 查看当前项目依赖包
go list
2、项目下载所有依赖
## 第一步
go mod download
## 第二步
go mod tidy
## 第三步
go list -m -json all
## 第四步
go mod vendor
## 执行完以上操作后,如果项目引入包爆红,或者项目可以正常运行,但引入的包报红,则执行以下方法。
## 第一步
go mod tidy
## 第二步
go mod vendor
要是还是不行(一般来说到第2步就可以了): 打开 GoLand 点击左上角: File–>Setting–>Go–>Go Modules–>Enable Go modules integration, 一般来说这样就可以了