go语言基础


#go是静态类型语言,一旦变量被声明,它的类型就不可能被改变
#变量存在有效区域,例如在花括号内的范围,子括号继承父括号
var bianliang = 10 #普通声明变量
bianliang := 10 #短声明,和普通声明一样,只可以在函数内使用,但是可以在无法使用var方法的情况中使用
#需要注意的是,如果要和浮点数进行计算,那么一开始就要指定为浮点数:
var bianliang float64 = 10

varint-- #等于varint = varint -1
varint++ #等于varint = varint +1
|| #或
&& #和
!var #变量前加感叹号相反,例如!= 就是不等于

if> else if>else #判断,在go中else if 必须和上一行的结尾在同一行不可单独一行

go
	if bianliang == "你好" { //判断
		fmt.Print("变量是你好") //打印
	} else if bianliang == "不好" { //否则再判断
		fmt.Print("变量是不好")
	} else if bianliang == "不好啦" { //否则再判断
		fmt.Print("变量是不好啦")
	}else { //否则
		fmt.Print("hello world!")
	}

switch>case>default #类似if 对同一个变量不停的比较,它还有一个fallthrough参数,放到某个case 中,如果匹配了这个case则会把下一个case也执行

go
var bian = "你好"
switch bian {
case "你好啦":
	fmt.Print("变量是:你好啦")
case "你好":
	fmt.Print("变量是:你好")
	fallthrough  //将会把下面的case也执行
case "你好么":
	fmt.Print("变量是:你好么")
default:
	fmt.Print("未匹配到结果后默认执行")
}

for #循环,如果没有条件则无限循环,附带条件则根据条件循环,使用break跳出循环

go
	var bian = 10
	for bian > 0 {  //限制条件内循环
		fmt.Println(bian)
		bian--
	}


for i := int64(0); i < 5; i++ {
	//循环5次
}

nil #代表0或空,同python的None一样

fmt.Println() #打印,换行
fmt.Print() #打印,不会换行
log.Printf("购物车数据: %+v\n", ordermx_cha) #格式化打印数组和对象

整数类型

int #32位上默认为int32,64位设备上默认为int64
uint #同上
int8 #-128127 #数字范围
uint8 #0
255
int16 #-3276832767
uint16 #0
65535
int32 #-21474832147483647
uint32 #0
4294967295
int64 #-922337203685477592233720368543775807
uint64 #0
18446744073709551615

超大数值

go
import (
	"fmt"
	"math/big"
)

func main() {
	bian := big.NewInt(9959)  //添加新的big变量
	bian2 := big.NewInt(9999) //添加新的big变量
	result := new(big.Int) //创建一个新值类型为big
	result.Add(bian, bian2)  //执行相加操作
	fmt.Print(result)  //打印结果
	
	//假设bian2为int6类型,可以使用 new(big.Int).SetInt64(int64(bian2))转换为big类型
}

数值字符串互转

go
import (
	"fmt"
	"strconv"
)

//数值转字符串
func main() {
	aa := strconv.FormatUint(dsdfd) //uint转字符串
	aab := strconv.FormatUint(dsdfd,10) //uint64转字符串
	ddd := 5555
	eee := zhustrconv.Itoa(ddd) //Int转字符串
	fmt.Sprint(dgform.Shangpin)  //数值转字符串
	aaa := "哈哈"
	fff := fmt.Sprintf("这里是字符串 %v 插入", aaa) //通过占位符插入,其中v代表默认值,f代表浮点数类型,d代表整数类型
	bbb := "我很好"
	ccc := eee + fff + bbb
	fmt.Println(ccc)
}

//字符串转数值
func ceshi() {
	str := "3.14159"                      //浮点数字符串
	f, err := strconv.ParseFloat(str, 64) //字符串转浮点数,并接受错误,第二参数为转换后的位数是64位
	if err != nil {
		fmt.Println("转换失败:", err)
	} else {
		fmt.Printf("浮点数值: %f\n", f)
	}
}

func ceshi2() {
	str1 := "42"
	i, err := strconv.ParseInt(str1, 10, 64) //字符串转int,10进制,64位
	if err != nil {
		fmt.Println("转换失败:", err)
	} else {
		fmt.Printf("整数值: %d\n", i)
	}

	str2 := "255"
	u, err := strconv.ParseUint(str2, 10, 64) //字符串转Uint
	if err != nil {
		fmt.Println("转换失败:", err)
	} else {
		fmt.Printf("无符号整数值: %d\n", u)
	}
}

字符串拼接

fmt.Sprintf 使用占位符拼接不同类型的字符,并返回字符串,常用的占位符有
%s:用于字符串(string)。它将相应的变量作为字符串进行插入。
%d:用于整数(int、int32、int64 等)。它将相应的变量作为十进制数进行插入。
%f:用于浮点数(float32、float64)。你可以指定小数点后的精度,例如 %.2f 用于格式化浮点数并保留两位小数。
%t:用于布尔值(bool)。它将 true 或 false 插入到字符串中。
%v:万用占位符。它会根据相应变量的类型自动选择格式。对于大多数变量,它等同于使用默认格式。
%+v:类似于 %v,但对于结构体,它还会添加字段名。
%#v:用于打印值的 Go 语言语法表示。例如,对于字符串,它会包含引号。
%p:用于指针,输出指针的内存地址。

小数转换

golang
//保留4位小数,并附带转为字符串格式了
	cny2 := fmt.Sprintf("%.4f", cnyf)
	//如果不足4位,去掉尾部多余0
	cny := strings.TrimRight(cny2, "0")

一个完整的简易示范

go
package main

import (
	"fmt"
)

func main() { //花括号代表程序内,变量也定义到程序内
	// var bianliang string //定义类型
	// bianliang = "你好"     // 赋值
	var bianliang = "你好"  //定义变量并赋值
	if bianliang == "你好" { //判断
		fmt.Print("变量是你好") //打印
	} else if bianliang == "不好" { //否则再判断
		fmt.Print("变量是不好")
	} else { //否则
		fmt.Print("hello world!")
	}
}

指针

//&号指针的值,*号代表指针的地址
var a *int //声明一个变量指针
&a //代表指针的值

可以理解为 *就是留了一个空房子的门牌号,只是里面没住人,可以晚点给它赋值,而&则代表房子里面的人

语言结构体,相当于类

//结构体的作用是用于多组相同结构的数据

go
package main

import "fmt"

type Books struct { //定义一个结构体
   Title   string //结构体首字母命名一定要大写
   Author  string //定义结构体的类型和变量
   Subject string
   Book_id int
}

func main() {
   var aa Books    // 申明一个变量为Books类型的结构体
   aa.title = "标题" //通过对象访问子类
   aa.author = "作者"
   aa.book_id = 214
   aa.subject = "主题"
   fmt.Println(aa)
}

//通过结构体,访问子类的函数

go
package main

import "fmt"

type Books struct { //定义一个结构体
}

type Books2 struct {
}

func (t *Books) fangfa() { //定义一个匿名函数,如果将参数类型指定某个结构体,并定义一个方法,则该可以通过对象执行这个子方法
	fmt.Println("这是book1")
}

func (t *Books2) fangfa() {
	fmt.Println("这是book2")
}

func main() {
	var aa Books // 申明一个变量为Books类型的结构体
	aa.fangfa() //通过对象访问子类方法
}

//通过接口来抽象化结构体

go
package main

import "fmt"

type jiekou interface {
	fangfa()
}

type Books struct { //定义一个结构体
}

type Books2 struct { //定义另一个结构体
}

func (t *Books) fangfa() { //定义一个结构体类型方法
	fmt.Println("这是book1")
}

func (t *Books2) fangfa() { //定义另一个结构体类型方法
	fmt.Println("这是book2")
}

func main() {
	var aa jiekou // 申明一个变量为jiekou的接口类型
	aa = &Books{} //将变量指定为Books类型
	aa.fangfa()   //访问方法
}

使用接口和直接使用结构体有两个不同:
多态性:当一个变量指定为接口类型后,就可以在不同的结构体类型之间变换类型。
扩展性:在以后改动代码的时候,例如增加了新的结构体方法,只需要改更少的代码,直接将变量改成不同的结构体类型赋值即可

定义函数与返回值

go
package main

import (
	"fmt"
)

func main() {
	a, b, c := ceshi(5, 6, "哈哈")
	fmt.Print(a, b, c)
}

func ceshi(num1, num2 int, z string) (int, int, string) { //参数一二为整数,参数三为字符串,返回自如内容为 整数 整数,字符串
	aaa := num1 + num2
	bbb := num1 - num2
	ccc := z
	return aaa, bbb, ccc
}

快速定义函数实参

go
package main

import (
   "fmt"
   "math"
)

func main(){
   /* 声明函数变量 */
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   /* 使用函数 */
   fmt.Println(getSquareRoot(9))

}

并发

在调用子函数的时候,加上 go 即可

go
go f(x, y, z) //它会在一个新的协程中执行f函数

获取协程返回值使用通道

go
package main

import (
    "fmt"
    "time"
)

// 假设这是一个发送邮件的函数,并返回一个表示成功或失败的布尔值
func sendEmail() bool {
    // 模拟邮件发送的耗时操作
    time.Sleep(2 * time.Second)
    return true // 假设邮件发送成功
}

func main() {
    // 创建一个传递布尔值的通道
    ch := make(chan bool)

    // 启动一个协程来发送邮件
    go func() {
        // 调用 sendEmail 并将结果发送到通道
        ch <- sendEmail()
    }()

    // 从通道接收返回值
    result := <-ch

    // 打印结果
    fmt.Println("邮件发送状态:", result)

    // 关闭通道(可选,但是在大型应用中是一个好习惯)
    close(ch)
}

接口类型,看不懂还需要研究

go
package main

import (
	"fmt"
)

// 定义一个接口类型 Phone,该接口要求实现一个 call 方法
type Phone interface {
	call()
}

// 定义一个结构体类型 NokiaPhone,表示诺基亚手机
type NokiaPhone struct {
}

// 实现 Phone 接口的 call 方法,用于输出诺基亚手机的呼叫信息
func (nokiaPhone NokiaPhone) call() {
	fmt.Println("我是诺基亚手机,我可以给你打电话!")
}

// 定义一个结构体类型 IPhone,表示iPhone手机
type IPhone struct {
}

// 实现 Phone 接口的 call 方法,用于输出iPhone手机的呼叫信息
func (iPhone IPhone) call() {
	fmt.Println("我是iPhone手机,我可以给你打电话!")
}

func main() {
	// 声明一个变量 phone,它是一个 Phone 接口类型的变量
	var phone Phone

	// 创建一个 NokiaPhone 类型的实例,并将其赋值给 phone 变量
	phone = new(NokiaPhone)

	// 调用 phone 变量的 call 方法,这里实际上调用了 NokiaPhone 结构体的 call 方法
	phone.call()

	// 将 phone 变量赋值为一个 IPhone 类型的实例
	phone = new(IPhone)

	// 再次调用 phone 变量的 call 方法,这次实际上调用了 IPhone 结构体的 call 方法
	phone.call()
}

包管理

  • go get -u github.com/gin-gonic/gin //下载包
  • go list -m all //查看所有包
  • 查看远程包下载的包路径:
    $GOPATH/pkg/mod/ //在go环境目录下

自己写包调用

包的注意事项:
一个文件夹下只能有一个包名,可以是多个go文件
导入包就直接导入文件夹,包不能互导,如果两个包都要用到的写到第三个包

假设项目根目录下有子目录routers,然后创建了一个router.go的文件:

go
package router  //调用的时候会已这个包名为准,和文件名无关

import (
	"github.com/gin-gonic/gin" //导入gin包
	"net/http"
)

func Router() *gin.Engine {  //创建了一个路由函数,并指定类型为gin引擎
	r := gin.Default() //初始化gin

	r.GET("/", func(ctx *gin.Context) { //定义了一个路由和方法,定义了一个*gin.Context的上下文参数,类似django视图的request参数
		ctx.String(http.StatusOK, "你好,gin") //// 返回一个HTTP响应成功状态,状态码为200
	})
	
	user := r.Group("/user")  //创建分组路由
	{
		user.POST("/post", func(ctx *gin.Context) { //继承分组路由,此时这里应是/user/post
			ctx.String(http.StatusOK, "这是post")
		})
	
		user.GET("/", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "这是get分组")
		})
	
		user.DELETE("/del", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "这是del")
		})
	}
	return r //返回r
}

假设根目录下有main.go:

go
package main

import (
	"GinDemo/routers" //导入routers目录下的包
)

func main() {
	r := router.Router() //引用包名router下的Router方法
	r.Run(":80") // 启动HTTP服务器,监听端口80
}

遍历数组

go
//i是遍历的索引号,name是内容
for i, name := range names {
	fmt.Print(name)
}

创建空数组:msg_About := make([]string, 11)
数组追加:Urls = append(Urls, lists1) //后面参数是追加内容

map映射

golang
//初始化一个map变量,键值类型为uint,值类型为uint16
mapvar := make(map[uint]uint16) 
//遍历某个数组
for _, cart := range tjcart.Carts {
	为mapvar的键值赋值
	mapvar[cart.Cartid] = cart.Number
	}

常见错误排查

no new variables on left side of :=
左侧没有新变量
检查是否应该使用 = 而不是:=,例如变量err在之前已经被定义,则因该使用=,但是往往err伴随其他返回值,所以要么先定义好其他返回值的变量,然后使用=,或者直接将err重新命名使用 :=

error
too many return values
	have (error)
	want ()

返回值太多
应该是函数顶部没有定义返回的类型,而返回了值,或者返回类型和数量和定义的不匹配

永远记住你是独一无二的正如其他人一样 -- 玛格丽特·米德