本文章主要是写自己在做这个项目时候遇到的一些困难,如果都是做这个项目的(后端),可以看看
这个是项目网址
gin-vue-admin : https://github.com/flipped-aurora/gin-vue-admin
在此表示对大神奇淼的敬佩
首先,我们需要去下载一个cnpm的一个东西
我是看这个博客,写的贼好
npm和cnpm(windows)安装步骤_安装cnpm-CSDN博客
然后就去vs里面执行了,我还以为没执行成功,把终端输出的东西给chatgpt看了一下,显示成功了
然后我进入了这个server项目嘛,但是之前的go感觉有些要补一下,就先开始了视频教程里面的golang教程(选择性),学语言就是这样的,学了又忘
学习就是一个重复的过程(在做项目的时候有时候会对自己产生怀疑,呜呜呜)
回忆一下
一定要以go.mod为根目录
var a string ="hello 2002"
//关键字 变量名 变量类型 = 变量值
/*
关键字不能作为变量名
*/
//注意大写字母表示可以被其他包访问
//改变包的名称
cool "goclass/pkg" //将包名改成cool
/*
将一个包名完全引入
*/
. "goclass/pkg"
//这样子我们可以访问一个包中的私有属性
基本数据类型
整数类型
我们一般直接使用uint 和int类型,如果你的电脑是64位,就会变成int64,如果是32,就会使int32.
uint只能是正数
浮点类型 :float64(一般使用这个) float32
后面跟的是精确度,就是小数点后面有几个数
只要类型不同,就会直接报错
怎么判断是什么类型?
var str string
str = "I'm a string"
fmt.Printf("%T",str)
数据类型的转化→使用一个包 strconv
package mainimport ("fmt""strconv"
)func main() {var str stringstr = "I'm a string"fmt.Printf("%T\n", str)num, _ := strconv.Atoi(str)fmt.Printf("转换str之后%T\n", num)num2 := 456str = strconv.Itoa(num2)fmt.Printf("%T\n", str)str = "3.14"f, _ := strconv.ParseFloat(str, 64)fmt.Printf("%T\n", f)f = 2.731str = strconv.FormatFloat(f, 'E', -1, 64)fmt.Printf("%T\n", str)str = "true"b, _ := strconv.ParseBool(str)fmt.Printf("%T\n",b)}
听视频,复习的感觉跟第一遍学的时候完全 不一样啊
复杂数据类型
不能以数字,特殊标记(¥)开头
流程控制语句
a:=0
//没有前增 ++a / --a
//只有后增,后减
a++
a--func main() {a := 1switch a {case 1:fmt.Println(a)fallthrough/*注意fallthrough并不会执行case2的判断语句*/case 2:fmt.Println("fallthrough之后的结果")default:fmt.Println("都不成立")}}/*
以前我一直觉得死循环没用,直到我刷了几个算法题
*/
/*
好的又复习到一个不记得的
continue是跳出本次循环
*/
a:=0
for a<10{a++if (a==5){continue}fmt.Println(a)//不会输出5
}//goto
//还有这个,wqfunc main() {a := 1
A: //之前可能阅读那个书籍的时候没有彻底阅读流程部分for a < 10 {a++if a == 5 {break Agoto B}fmt.Println(a)//不会输出5}
B:fmt.Println("我来到B了")}
数组:把同一类元素放在一起的集合
a := [3]int{1, 2, 3}
//[元素长度]元素类型{元素1,元素2...}fmt.Println(a)a := [3]int{1, 2, 3}fmt.Println(a)
//当我们不知道有多少个数的时候
b := [...]int{2423, 4, 3, 43, 4, 3, 4, 3, 43, 4}
//直到需要多杀个数,但是不知道具体值的时候var d = new([10]int)d[5] = 3fmt.Println(a, b, d)
//注意打印d的时候会含有&
//因为使用new创建的是一个指针
对同一类东西做同一个操作的时候,会很方便
len(数组) == cap(数组)
a := [3]int{1,2,3}
cl := a[:]
cl = a[1:]package mainimport "fmt"func main() {//数组的len和cap是一样的,但是切片不是a := [3]int{1, 2, 3}cl := a[:]fmt.Println("初始len and cap", len(cl), cap(cl))cl = append(cl, 5)fmt.Println(len(cl), cap(cl))cl = append(cl, 5)cl = append(cl, 5)cl = append(cl, 5)cl = append(cl, 5)fmt.Println(len(cl), cap(cl)) //cap到达一定的值会自动扩充到某个值//这里了解即可
}
//数组的len和cap是一样的,但是切片不是a := [3]int{1, 2, 3}cl := a[:]//讨论copy/*将后面的某一段复制到前面的一段,如果没有特定标记就是从0开始*/cl1 := a[1:]copy(cl, cl1)fmt.Println(cl) //expect 233a := [3]int{1, 2, 3}cl := a[:]//讨论copy/*如果说我们后面copy的数组len超过了前面的len,就会直接覆盖*/cl1 := append(cl, 5)cl1 = append(cl1, 5)copy(cl, cl1)fmt.Println(cl1)package mainimport "fmt"func main() {//数组的len和cap是一样的,但是切片不是a := [3]int{1, 2, 3}cl := a[:]//讨论copy/*如果我们想要指定覆盖*/cl1 := a[2:]copy(cl[1:2], cl1)fmt.Println(cl)
}//创造切片的方式
var aa []int//空数组aa := append(aa,5)aaa := make([]int,4) //有默认值
map
package mainimport "fmt"func main() {/*三种声明方式*/var m map[string]stringm = map[string]string{}m["name"] = "xzc"m["sex"] = "男"m2 := map[int]bool{}m2[1] = truem2[2] = falsem3 := make(map[string]string)m3["sex"] = "女"m4 := map[int]interface{}{} //规定入参结构和出参结构就行m4[1] = 1m4[2] = falsem4[3] = "xzc"fmt.Println(m4)//delete(变量名,key)delete(m4, 1)fmt.Println(m4)//当前长度fmt.Println(m["sex"])//通过for range 找到它的for k, v := range m4 {fmt.Println(k, v)}
}
func
func 函数名(入参1 入参类型,入参2 入参类型)(出参1 出参类型,出参2 出参类型)package mainpackage mainimport "fmt"/*func bb(data1 string) {fmt.Println(data1)}
*/
func main() {b := func(data1 string) {fmt.Println(data1)}/*大概就是b := bb*/b("Fuck")mo(1, "2", "23", "32")a := []string{"2", "3", "4", "5"}//将切片传入mo(2, a...)
}// 不知道入参有多少个,这样的我们成为不定项参数,一定要放在最后一个
func mo(data1 int, data2 ...string) {fmt.Println(data2)for k, v := range data2 {fmt.Println(k, v)}
}func A(data1, data2 int) (res1 string, res2 int) {//我们没有在函数内部定义res1 和 res2/*但是Go会自动帮助我们var ret1 stringvar res2 int*/return //省略 return res1,res2
}
func main() {//延迟调用函数defer last()//自知型函数//自动执行(func() {fmt.Println("我在这里执行,别人都管不着")})()mo()(4)
}// 闭包函数
func mo() func(int) {return func(num int) {fmt.Println("成功声明闭包函数", num)} //返回的要和回参一毛一样
}
func last() {fmt.Println("我要最后执行")
}
指针和地址
package mainimport "fmt"func main() {var a inta = 123fmt.Println(a)var b *int/*b = a指针不能指向数据只能指向具体值的地址*/b = &a // &内存地址fmt.Println(a, *b)*b = 456 //*内存地址fmt.Println(a, *b)}
package mainimport "fmt"func main() {//数组指针var a [5]stringa = [5]string{"1", "2", "3", "4", "5"}var aP *[5]stringaP = &afmt.Println(a, aP)//指针数组var arrP [5]*stringstr1 := "str1"str2 := "str2"str3 := "str3"str4 := "str4"str5 := "str5"arrP = [5]*string{&str1, &str2, &str3, &str4, &str5}for k, v := range arrP {fmt.Println(k, *v)}*arrP[3] = "333" //修改指针fmt.Println(str4)
}
package mainimport "fmt"func main() {var str1 = "我定义了"pointFunc(&str1)//指针传参fmt.Println(str1)
}func pointFunc(p1 *string) {*p1 = "我变了"
}
在map当中,我们通常不需要设置指针,map本身就是引用类型
struct
结构体是一个可以存储不同数据类型的数据类型
package mainimport "fmt"type Qm struct {Name stringSex stringHobbies []string
}func main() {/*三种声明方式var qm Qmqm.Name = "qm"qm.Sex = "男"qm.Hobbies = []string{"唱歌", "打游戏"}*//*qm := Qm{"xzc", "男", []string{"打游戏"},}*///建议用这种方法qm := Qm{Name: "xzc",Sex: "男",Hobbies: []string{"11223", "342"},}qmFunc(qm)/*qm := new(Qm)//声明了一个Qm的空结构体(类似于java实例)fmt.Println(qm)*/}func qmFunc(qm Qm) {fmt.Println(qm)
}
qm := Qm{Name: "xzc",Sex: "男",Hobbies: []string{"11223", "342"},}qmPtr := &qm.Name*qmPtr = "zcx" //会被看成*(qmPtr.Name),但实际上qmPtr是指向的qmfmt.Println(qm)qmPtr2 := &qm(*qmPtr2).Name = "zcx2"fmt.Println(qm)
结构体可以有自己的方法
package mainimport "fmt"type Qm struct {Name stringSex stringHobbies []stringHome //复制进来,直接归属Qm
}type Home struct {P string
}func (h *Home) Open() {fmt.Println("open the", h.P)
}
func (q *Qm) Song(name string) (restr string) {fmt.Printf("%s唱了一首%v", q.Name, name)return "观众觉得很好听"
}
func main() {qm := Qm{Name: "xzc",Sex: "男",Hobbies: []string{"11223", "342"},}qmPtr := &qm.Name*qmPtr = "zcx" //会被看成*(qmPtr.Name),但实际上qmPtr是指向的qmfmt.Println(qm)qmPtr2 := &qm(*qmPtr2).Name = "zcx2"fmt.Println(qm)restr := qm.Song("惊雷")fmt.Print(restr)qm.P = "北京"fmt.Println(qm)qm.Open() //连他的方法都可以用
}
interface 接口
是一系列方法的集合,是一个规范
/* var a Animalc := Cat{Name: "Tome",Sex: "男",}a = ca.Eat()a.Run()*/var a Animala = Cat{Name: "Tome",Sex: "男",}a.Eat()a.Run()
package mainimport "fmt"type Animal interface {Eat()Run()
}type Cat struct {Name stringSex string
}type Dog struct {Name string
}// Cat is an animal
func (c Cat) Run() {fmt.Println(c.Name, "开始跑")
}func (c Cat) Eat() {fmt.Println(c.Name, "开始吃")
}
func (c Dog) Run() {fmt.Println(c.Name, "开始跑")
}func (c Dog) Eat() {fmt.Println(c.Name, "开始吃")
}
func main() {c := Dog{Name: "spake"}Myfunc(c)
}func Myfunc(a Animal) {//这就是泛型a.Eat()a.Run()
}
package mainimport "fmt"type Animal interface {Eat()Run()
}type Cat struct {Name stringSex bool
}type Dog struct {Name string
}// Cat is an animal
func (c Cat) Run() {fmt.Println(c.Name, "开始跑")
}func (c Cat) Eat() {fmt.Println(c.Name, "开始吃")
}
func (c Dog) Run() {fmt.Println(c.Name, "开始跑")
}func (c Dog) Eat() {fmt.Println(c.Name, "开始吃")
}var L Animalfunc main() {c := Cat{Name: "Tom",Sex: true,}Myfunc(c)L.Run()}
func Myfunc(a Animal) {L = a
}
goroutine and channel
Goroutine 是 Go 语言中的一种轻量级线程,它可以在相同的地址空间中并发执行多个函数。Goroutine 的创建和调度由 Go 运行时系统自动管理,不需要用户手动进行线程管理。
package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupwg.Add(1) //协程管理器go Run(&wg)wg.Wait()
}func Run(wg *sync.WaitGroup) {fmt.Println("我跑起来了")wg.Done()
}
package mainimport "fmt"func main() {c1 := make(chan int)c1 <- 1fmt.Println(<-c1) //这里会被阻塞,因为c1没有缓冲区无法存放1,// 但是<-c1表示我需要从c1管道中找到一个值
}
package mainimport "fmt"func main() {c1 := make(chan int, 5)go func() {for i := 0; i < 10; i++ {c1 <- i //先会存5个,被取了之后,立马存}}()for i := 0; i < 10; i++ {fmt.Println(<-c1)} /*这里其实是等待c1通道发送消息过来*/
}
package mainimport "fmt"func main() {c1 := make(chan int, 5)/*var read <-chan int = c1var write chan<- int = c1close(c1)*/c1 <- 1c1 <- 2c1 <- 3c1 <- 4c1 <- 5close(c1)for v := range c1 {fmt.Println(v)//如果不关闭通道,接收方会一直读取}}
package mainimport "fmt"func main() {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)ch1 <- 1ch2 <- 2ch3 <- 3select { //随机执行case <-ch1:fmt.Println("ch1")case <-ch2:fmt.Println("ch2")case <-ch3:fmt.Println("ch3")default:fmt.Println("都不满足")}}
package mainimport "fmt"func main() {c := make(chan int)var read <-chan int = cvar write chan<- int = cgo SetChan(write)GetChan(read)
}
func SetChan(write chan<- int) {for i := 0; i < 10; i++ {fmt.Println("我是在write函数中")write <- i}
}
func GetChan(read <-chan int) {for i := 0; i < 10; i++ {fmt.Println("我从chan中取出", <-read)}
}
断言和反射
断言:把一个接口类型调用为原始结构并且可以使用原始结构的方法和属性
package mainimport "fmt"type User struct {Name stringAge intSex bool
}
type Student struct {UserClass string
}func (u User) SayName(name string) {fmt.Println("我的名字叫做", name)
}
func main() {u := User{"许智超",18,true,}check(u)//人为我们已经知道传入的是User
}
func check(v interface{}) {switch v.(type) {case User:fmt.Println(v.(User).Name)fmt.Println("我是User")case Student:v.(Student).SayName(v.(Student).Class)fmt.Println("我是Student")}//我们这些很麻烦//反射应运而生,不需要使用switch//可以直接知道它的类型
}
func check(inter interface{}) {t := reflect.TypeOf(inter) //类型v := reflect.ValueOf(inter) //所有值for i := 0; i < t.NumField(); i++ {//NumField是指结构体中有几个值fmt.Println(v.Field(i))}fmt.Println(t, v)
}func check(inter interface{}) {v := reflect.ValueOf(inter) //所有值fmt.Println(v.FieldByName("Class"))
}
package mainimport ("fmt""reflect"
)type User struct {Name stringAge intSex bool
}
type Student struct {UserClass string
}func (u User) SayName(name string) {fmt.Println("我的名字叫做", name)
}
func main() {u := User{"许智超",18,true,}s := Student{u, "三年二班"}check(&s)fmt.Println(s)//人为我们已经知道传入的是User
}
func check(inter interface{}) {v := reflect.ValueOf(inter)e := v.Elem()//要使用下面的需要传入指针e.FieldByName("Class").SetString("四年二班")fmt.Println(inter)
}
package mainimport ("fmt""reflect"
)type User struct {Name stringAge intSex bool
}
type Student struct {UserClass string
}func (u User) SayName(name string) {fmt.Println("我的名字叫做", name)
}
func main() {u := User{"许智超",18,true,}check(u)
}
func check(inter interface{}) {v := reflect.ValueOf(inter)m := v.Method(0)m.Call([]reflect.Value{reflect.ValueOf("大牛逼")})
}
day 2从Mutex继续开始