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 必须和上一行的结尾在同一行不可单独一行
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也执行
var bian = "你好"
switch bian {
case "你好啦":
fmt.Print("变量是:你好啦")
case "你好":
fmt.Print("变量是:你好")
fallthrough //将会把下面的case也执行
case "你好么":
fmt.Print("变量是:你好么")
default:
fmt.Print("未匹配到结果后默认执行")
}
for #循环,如果没有条件则无限循环,附带条件则根据条件循环,使用break
跳出循环
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 #数字范围255
uint8 #0
int16 #-327683276765535
uint16 #0
int32 #-214748321474836474294967295
uint32 #0
int64 #-92233720368547759223372036854377580718446744073709551615
uint64 #0
超大数值
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类型
}
数值字符串互转
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:用于指针,输出指针的内存地址。
小数转换
//保留4位小数,并附带转为字符串格式了
cny2 := fmt.Sprintf("%.4f", cnyf)
//如果不足4位,去掉尾部多余0
cny := strings.TrimRight(cny2, "0")
一个完整的简易示范
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
//代表指针的值
可以理解为 *就是留了一个空房子的门牌号,只是里面没住人,可以晚点给它赋值,而&则代表房子里面的人
语言结构体,相当于类
//结构体的作用是用于多组相同结构的数据
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)
}
//通过结构体,访问子类的函数
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() //通过对象访问子类方法
}
//通过接口来抽象化结构体
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() //访问方法
}
使用接口和直接使用结构体有两个不同:
多态性:当一个变量指定为接口类型后,就可以在不同的结构体类型之间变换类型。
扩展性:在以后改动代码的时候,例如增加了新的结构体方法,只需要改更少的代码,直接将变量改成不同的结构体类型赋值即可
定义函数与返回值
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
}
快速定义函数实参
package main
import (
"fmt"
"math"
)
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9))
}
并发
在调用子函数的时候,加上 go 即可
go f(x, y, z) //它会在一个新的协程中执行f函数
获取协程返回值使用通道
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)
}
接口类型,看不懂还需要研究
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的文件:
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:
package main
import (
"GinDemo/routers" //导入routers目录下的包
)
func main() {
r := router.Router() //引用包名router下的Router方法
r.Run(":80") // 启动HTTP服务器,监听端口80
}
遍历数组
//i是遍历的索引号,name是内容
for i, name := range names {
fmt.Print(name)
}
创建空数组:msg_About := make([]string, 11)
数组追加:Urls = append(Urls, lists1) //后面参数是追加内容
map映射
//初始化一个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重新命名使用 :=
too many return values
have (error)
want ()
返回值太多
应该是函数顶部没有定义返回的类型,而返回了值,或者返回类型和数量和定义的不匹配