本文章主要是写自己在做这个项目时候遇到的一些困难,如果都是做这个项目的(后端),可以看看
这个是项目网址
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 main
import (
"fmt"
"strconv"
)
func main() {
var str string
str = "I'm a string"
fmt.Printf("%T\n", str)
num, _ := strconv.Atoi(str)
fmt.Printf("转换str之后%T\n", num)
num2 := 456
str = strconv.Itoa(num2)
fmt.Printf("%T\n", str)
str = "3.14"
f, _ := strconv.ParseFloat(str, 64)
fmt.Printf("%T\n", f)
f = 2.731
str = 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 := 1
switch 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
//还有这个,wq
func main() {
a := 1
A: //之前可能阅读那个书籍的时候没有彻底阅读流程部分
for a < 10 {
a++
if a == 5 {
break A
goto 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] = 3
fmt.Println(a, b, d)
//注意打印d的时候会含有&
//因为使用new创建的是一个指针
对同一类东西做同一个操作的时候,会很方便
len(数组) == cap(数组)
a := [3]int{1,2,3}
cl := a[:]
cl = a[1:]
package main
import "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 233
a := [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 main
import "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 main
import "fmt"
func main() {
/*
三种声明方式
*/
var m map[string]string
m = map[string]string{}
m["name"] = "xzc"
m["sex"] = "男"
m2 := map[int]bool{}
m2[1] = true
m2[2] = false
m3 := make(map[string]string)
m3["sex"] = "女"
m4 := map[int]interface{}{} //规定入参结构和出参结构就行
m4[1] = 1
m4[2] = false
m4[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 main
package main
import "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 string
var 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 main
import "fmt"
func main() {
var a int
a = 123
fmt.Println(a)
var b *int
/*b = a
指针不能指向数据
只能指向具体值的地址
*/
b = &a // &内存地址
fmt.Println(a, *b)
*b = 456 //*内存地址
fmt.Println(a, *b)
}
package main
import "fmt"
func main() {
//数组指针
var a [5]string
a = [5]string{"1", "2", "3", "4", "5"}
var aP *[5]string
aP = &a
fmt.Println(a, aP)
//指针数组
var arrP [5]*string
str1 := "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 main
import "fmt"
func main() {
var str1 = "我定义了"
pointFunc(&str1)//指针传参
fmt.Println(str1)
}
func pointFunc(p1 *string) {
*p1 = "我变了"
}
在map当中,我们通常不需要设置指针,map本身就是引用类型
struct
结构体是一个可以存储不同数据类型的数据类型
package main
import "fmt"
type Qm struct {
Name string
Sex string
Hobbies []string
}
func main() {
/*
三种声明方式
var qm Qm
qm.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是指向的qm
fmt.Println(qm)
qmPtr2 := &qm
(*qmPtr2).Name = "zcx2"
fmt.Println(qm)
结构体可以有自己的方法
package main
import "fmt"
type Qm struct {
Name string
Sex string
Hobbies []string
Home //复制进来,直接归属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是指向的qm
fmt.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 Animal
c := Cat{
Name: "Tome",
Sex: "男",
}
a = c
a.Eat()
a.Run()*/
var a Animal
a = Cat{
Name: "Tome",
Sex: "男",
}
a.Eat()
a.Run()
package main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
Sex 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 main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
Sex 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 Animal
func 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 main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1) //协程管理器
go Run(&wg)
wg.Wait()
}
func Run(wg *sync.WaitGroup) {
fmt.Println("我跑起来了")
wg.Done()
}
package main
import "fmt"
func main() {
c1 := make(chan int)
c1 <- 1
fmt.Println(<-c1) //这里会被阻塞,因为c1没有缓冲区无法存放1,
// 但是<-c1表示我需要从c1管道中找到一个值
}
package main
import "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 main
import "fmt"
func main() {
c1 := make(chan int, 5)
/*var read <-chan int = c1
var write chan<- int = c1
close(c1)*/
c1 <- 1
c1 <- 2
c1 <- 3
c1 <- 4
c1 <- 5
close(c1)
for v := range c1 {
fmt.Println(v)//如果不关闭通道,接收方会一直读取
}
}
package main
import "fmt"
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch3 := make(chan int, 1)
ch1 <- 1
ch2 <- 2
ch3 <- 3
select { //随机执行
case <-ch1:
fmt.Println("ch1")
case <-ch2:
fmt.Println("ch2")
case <-ch3:
fmt.Println("ch3")
default:
fmt.Println("都不满足")
}
}
package main
import "fmt"
func main() {
c := make(chan int)
var read <-chan int = c
var write chan<- int = c
go 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 main
import "fmt"
type User struct {
Name string
Age int
Sex bool
}
type Student struct {
User
Class 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 main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Sex bool
}
type Student struct {
User
Class 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 main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Sex bool
}
type Student struct {
User
Class 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("大牛逼")})
}