go 语言基本语法

一、go语言初探
1. 基本数据类型 整型(int)、浮点型(float)、布尔型(bool)、字符串(string)
2. 复合数据类型 数组、切片、结构体、函数、map、通道(channel)、接口等
   值类型: 基本数据类型和数组都是值类型
   引用类型: 切片

3. 安装Bee go install github.com/beego/bee/v2@master

4. 命令:
    go version #查看go的安装版本
    go env #查看go运行环境
    go run main.go #运行go脚本
    go build main.go #生成main.exe 
    main.exe #执行生成的文件
    go fmt main.go #格式化代码 一般不用了, 编辑器可以格式化
    go get github.com/shopspring/decimal 下载go插件

二、语法:
1. fmt.Print #可以接收多个参数
2. fmt.Println #可以接收多个参数
3. fmt.Printf #格式化输出 类似c语言 %v 相应默认格式输出 %T 输出数据类型 int ....
4. fmt.Sprintf # 格式化输出到变量

4.定义变量 定义变量后必须使用否则会报错 建议用驼峰方式定义 小驼峰表示私有/大驼峰表示公有
    var a = 'aaa' 
    var a int = 10 
    a := 10 #类型推导方式定义变量 短变量声明法 不能声明全局变量

    #变量初始化的方式 同一作用域变量不能重复声明
        var name string
        name = "张三" #先定义再赋值的方式 必须用双引号
            或
        var name string = "张三" #直接赋值方式
            或
        var name = "张三" #类型推导方式 name := "张三" 短变量声明法


    #定义多个变量
        var a1, a2 string #同类型变量
        a1 = "张三"
        a2 = "李四"var ( #也可以使用类型推导方式
            username string
            age      int
            sex      string
        )
        username = "张三"
        age = 18
        sex = ""
            或
        a, b, c := 12, 13, "C" # 短变量声明多个变量

    #匿名变量 _

    var username string #变量不设置值 默认值为空
    var num int #变量不设置值 默认值为0

5. 常量 const pi = 3.14195 #定义常量必须赋值
    # 一次定义多个常量
    const (
        A = "A"
        B = "B"
    )
    const ( # 如果后面的常量不赋值, 则值等于前面的常量值 且 常量定义后不使用也不会报错
        name = "abc"
        name2
    )

    const ( # iota 常量自动累加 类似计数器
        a = iota # 0
        b # 1
        c # 2
    )

    const ( # iota 常量自动累加 类似计数器
        a = iota # 0
        _ # 跳过某个值
        c # 2
    )

    const ( # iota 常量自动累加 类似计数器
        a = iota # 0
        b = 100 # 100
        c # 2
        d # 3
    )

    const ( # 矩阵?
        a, b = iota + 1, iota + 2 #1 2
        c, d # 2 3
        e, f # 3 4
    )

6. 数据类型
    int类型 unsafe.Sizeof(a) 变量占用的存储空间(占用空间的位数) int类型尽量用具体的类型不要直接使用int, 因为int占用8字节会浪费内存空间
        有符号整型: int8 int16 int32 int64(默认)
        无符号整型: uint8 uint16 uint32 uint64
        不同位数不能进行加减运算, 运算前需进行类型转换如int64(a) 注意高位转低位溢出问题
        char字符: 属于int类型 值为字符对应的ascii码 用单引号设置值 fmt.Printf("%c",'a') 原样输出

    float浮点型
        float32 float64(默认)
        科学计数法: var a = 3.14e2 #314

    bool布尔型 默认值是false, bool无法转换成整型, 不能参与运算

    string字符串 要用双引号 字符用单引号
        str1 := `agsd` #原样字符串/多行字符串使用反引号, 类似js的模板字符串
        len(str) # 计算字符串占用存储空间, 一个中文占三个字节
        + # 拼接字符串
        strings.Split(str,"-") # 字符串分割成切片
        strings.Join(str,"-") # 切片连接成字符串
        strings.Contains(str1, "123") # 字符串是否包含另一个字符串 true/false
        strings.HasPrefix(str1, "123")/strings.HasSuffix(str1, "123") # 前辍/后辍是否包含某个字符串 true/false
        strings.Index(str1, "456") # 查找字符串第一次出现的位置(从前往后找) 找不到返回-1
        strings.LastIndex(str1, "456") # 查找字符串第一次出现的位置( )从后往前找) 找不到返回-1

        #循环输出字符串
        s := "你好 golang"
        for _, v := range s {
            fmt.Printf("%v(%c)", v, v)
        }

        #修改字符串
        s1 := "big"
        byteStr := []byte(s1) //转换成byte
        byteStr[0] = 'p' //修改字符串
        fmt.Println(string(byteStr)) //转回string pig

        s1 := "你好"
        byteStr := []rune(s1) //转换成rune
        byteStr[0] = '' //修改字符串
        fmt.Println(string(byteStr)) //转回string 大好
7. 基本数据类型转换: int8()、int16()、float32()、float64()
    str := fmt.Sprintf("%d",num) #可以使用Sprintf 把任意类型转换成字符串类型
    strconv.FormatInt(int64(a),10) # int转字符串
    strconv.FormatFloat(float64(a),'f',2,64) #浮点转字符串
    strconv.ParseInt # 字符串转int

    # 字符串转int
        str := "abc"
        num, err := strconv.ParseInt(str, 10, 64)

    # 字符串转float strconv.ParseFloat
    # 不建议把strng 转 bool

8. 运算符
    # int 相除 只保留整数部分
    # ++ -- 无前置运算符

9. 流程控制语句
    if: 条件不加括号, 大括号不能省略且不能换行
    # 没有while语句
    for: 条件不加括号

    # for rang 循环字符串
    str := "你好 golang"
    for _, v := range str {
        fmt.Printf("%c", v)
    }

    #for rang 循环切片 也可以用len计数循环
    arr := []string{"php", "java", "golang"}
    for k, v := range arr {
        fmt.Println(k, v)
    }

    switch case # case 1,2,3,5 可以这样写 可以不写break fallthrough 穿透一层 注: 其他语言不加break会自动穿透

    break/continue/goto: 使用 break label 可以跳出多个循环 continue label 可以跳出多个循环,但是还会继续执行循环 goto label 可以跳到指定位置

10. 数组(固定长度的切片) 数组类型为: [n]int [n]string go无array类型, 切片的类型为 []int []string, 切片是没有固定元素个数的数组吗? 
    # 定义数组 
    var arr [3]int # 定义数组 默认值为 [0 0 0]
    arr[0] = 100 //数组赋值
    arr[1] = 80 //数组赋值
    arr[2] = 90 //数组赋值

    #初始化数组
    var arr = [3]int{12, 23, 56}
    arr := [3]int{12, 23, 56}
    arr := [...]int{12, 23, 56} //数组类型仍为[3]int 只是编译器会自动推导数组元素的个数(建议用法) 虽然长度可以让编译器推导, 但是数组长度不可改变
    arr := [...]int{0: 2, 3: 3} //指定数组下标方式, 可以略过不指定的下标, 默认值为基础数据类型的默认值

    #数组遍历 for len循环/for range循环两种

    #二维数组/多维数组 数组的数据类型必须要一致
     var arr = [3][2]string{
         {"北京", "上海"}
         {"广州", "深圳"}
         {"成都", "重庆"}
     }

     #取数组中的元素
     arr1 := arr[:] # 取数组中的所有值赋值到变量 此时变量为切片
     arr1 := arr[1:4] # 取数组中第一个到第三个元素的值 左包右不包
     arr1 := arr[1:] # 取数组第一个元素到最后一个元素 左包右不包
     arr1 := arr[:3] #取数组前三个元素 左包右不包

11. 切片(可变长度的数组, 数据类型为引用数据类型) 切片的类型为 []int []string 默认值为[] 也是nil
    # 声明切片 把数组的长度去掉
    var arr []int # 定义切片 默认值为 []也是nil, 长度为0, 容量为0
    var arr = []int{1,23,45,8} # 声明并赋值

    #切片遍历 for len循环/for range循环两种

    #数组转切片
    var arr = [3]int{12, 23, 56}
    var slice = arr[:] # 取数组中的所有值

    #取切片中的数据(与数组相同)

    #切片长度len: 切片中元素的个数 容量cap: 切片第一个元素到底层数组的最后一个元素的个数

    # 使用make声明切片
    var sliceA = make([]int, 4, 8) # 声明长度为4 容量为8的切片

    # 修改切片 sliceA[0] = 10 # sliceA[4] = 20 会报错, golang中给切片扩容要用append方法

    # 切片操作
        # append插入数据
        sliceA = append(sliceA, 12, 12, 22) # 给切片插入数据, 不会因为扩容越界问题

        # append 合并切片
        sliceA := []string{"php", "java"}
        sliceB := []string{"nodejs", "go"}
        sliceC := append(sliceA, sliceB...) #合并切片 注意...

        # append 删除切片元素
        cliceC := append(slice[:2], slice[3:]...) # 删除index为2的元素

        # copy 复制切片
        sliceA := []string{"php", "java"}
        sliceB := make([]string, 2, 2)
        copy(sliceB, sliceA) # 把sliceA复制到sliceB

    # sort包可以对数组和切片进行排序

12. map 数据类型 属于引用数据类型 key并不是按照一定顺序排序的
    #创建map类型
    var userInfo = make(map[string]string) # 默认值为map[] 类型为map[string]string
    var userInfo = map[string]string{
        "username":"张三"
    }

    #map类型赋值 userInfo["username"] = "张三"

    # for range循环map

    v, ifExist := userInfo["username"] # ifExist bool true/false 判断map中的key是否存在

    delete(userInfo,"username") # 删除map中的key

13. 切掉与map类型结合使用
    # 切片的值是map (这种类型的数据结构和mysql表的结构基本一致)
    sliceMap := []map[string]string{ //数据类型为 []map[string]string
        {"username": "张三"},
        {"username": "李四"},
    }

    # map的值是切片
    mapSlice := map[string][]string{ // []string是个切片类型
        "hobby": {"吃饭", "睡觉"},
    }

    # 综合应用: 统计文字出现的个数
    str := "你好呀,这是一个好玩的开发语言好一"
    byteStr := []rune(str)              //字符串转rune类型
    var countNum = make(map[string]int) //map类型计数
    for _, v := range byteStr {
        char := fmt.Sprintf("%c", v) //rune 转文字
        countNum[char]++
    }
    fmt.Println(countNum)

14. 函数
    func sumInt(x ...int) # 参数可变函数 x类型是切片 如果不传参数为nil
    return a,b #返回值可以是多个

    func sumInt(x, y int) (sum int, sub int) { // 返回值可以提前定义
        sum = x + y
        sub = x - y
        return // 返回2值 sum 和 sub
    }

    # 定义函数类型(类似c语言的.h)
    type cal func(x, y int) int //定义一个函数类型

    func add(x, y int) int {
        return x + y
    }
    func main() {
        c := add
        fmt.Println(c(1, 2))
    }

    # 函数作为函数的参数进行传递
    func add(x, y int) int { /*加法函数*/
        return x + y
    }
    func sub(x, y int) int { /*减法函数*/
        return x - y
    }

    type calType func(int, int) int /*定义函数类型*/

    func cal(x, y int, cal calType) int { /*计算器方法 第三个参数为函数 也可以传个匿名函数*/
        return cal(x, y)
    }
    func main() {
        add := cal(2, 1, add) /*加法操作*/
        sub := cal(2, 1, sub) /*减法操作*/
        fmt.Println(add, sub)
    }

    # 函数作为函数返回值
    func add(x, y int) int { /*加法函数*/
        return x + y
    }
    func sub(x, y int) int { /*减法函数*/
        return x - y
    }

    type cal func(int, int) int

    func do(x string) cal { /*计算器方法 第三个参数为函数*/
        if x == "+" {
            return add
        } else if x == "-" {
            return sub
        } else {
            return nil
        }
    }
    func main() {
        a := do("+")(1, 2)
        fmt.Println(a)
    }

    # 匿名函数
    func () { //匿名自执行函数
        //...
    }()

    var fn = func () { //匿名函数赋值到变量
        //...
    }

    # 闭包

    # defer 延迟执行语句 先defer的语句最后执行
    # panic 抛出异常 recover 接收异常


15. 指针: 
    p := &a # 取变量a的地址赋值到p, p的类型为*int 指针变量
    a := *p # 根据指针变量取值赋值到a, a的类型为int

16. 自定义数据类型
    type cal func(x, y int) int //自定义函数类型 数据类型为cal
    type myInt int //自定义类型 数据类型为myInt var a myInt = 10 此时的数据类型为myInt 
    type myInt = int // 给类型起个别名 var a myInt = 10 此时的数据类型为int

    #结构体 (值类型)
        type Person struct { #定义结构体
            name string
            age  int
            sex  string
        }

        var p1 Person 或 var p2 = new(Person) 或 var p3 = &Person{} #实例化结构体
        p1.name = "张三" #赋值

        # 结构体自定义方法: 方法在结构体外面 还可以给自定义类型添加方法
        # 结构体嵌套(也叫结构体继承) 结构体里面数据可以是切片或map或结构体

    # json和结构体相互转换
        jsonByte, _ := json.Marshal(p)
        fmt.Println(string(jsonByte)) # 结构体转json

        var jsonStr = `{"Name":"张三","Age":12}`
        var p Person
        _ = json.Unmarshal([]byte(jsonStr), &p) # json转结构体

    # 结构体标签
        type Person struct {
            Name string `json:"id"` # 结构体转json的时候key的名字
            Age  int `json:"age"`
        }

17. 包管理工具 go mod 其他语言包管理工具: php composer / java maven / nodejs npm
    go mod init demo #生成go mod 配置文件
    # 第三方包 https://pkg.go.dev
    go get github.com/shopspring/decimal # 下载第三方包
    go mod download # 代码中加入引入包的路径 会自动下载第三方包到GOPATH目录下 GOPATH目录可以通过go env来查看
    go clean -modcache # 清除 go mod download 下载的第三方包
    go mod vendor # 复制第三方包到项目vendor中

18. 接口interface 一组函数的集合 空接口表示任意类型/函数参数可以使用空接口/map类型的值可以是空接口/切片的值也可以是空接口 !!!这不就是弱类型语言吗  
     结构体可以实现多个接口, 接口也可以嵌套
    type Usber interface {
        start()
        stop()
    }

    type Computer struct {

    }

    func (c Computer) work(usb Usber)  {
        usb.start()
        usb.stop()
    }

    type Phone struct {
        Name string
    }

    func (p Phone) start() {
        fmt.Println(p.Name, "启动")
    }
    func (p Phone) stop() {
        fmt.Println(p.Name, "关机")
    }

    func main() {
        var computer = Computer{}
        var phone = Phone{Name:"小米"}
        computer.work(phone)
    }

19. 断言: v, ok := a.(tring) # 是括号中的数据类型

20. 并发 goroutine channel
     1. 进程: 操作系统一次执行过程, 资源分配和调度的基本单位
     2. 线程: 进程的一个执行实例, 比进程更小能够独立运行的基本单位, 一个进程中可以有多个线程
     3. 并发: 多个线程竞争同一个位置
     4. 并行: 多个线程同时执行
     5. 协程: 用户线程 go func() # go 关键字 后面执行方法表示开启一个协程
                 主线程结束, go 协程会强制结束 协程等主线程, 但是主线程不会等协程

     6. 协程实例
     var wg sync.WaitGroup

    func test() {
        for i := 0; i < 10; i++ {
            fmt.Println("test() 你好golang", i)
            time.Sleep(time.Millisecond * 100)
        }
        wg.Done() /*协程计数器减1*/
    }

    func main() {
        wg.Add(1) /*协程计数器加1*/
        go test()
        for i := 0; i < 10; i++ {
            fmt.Println("main() 你好golang", i)
            time.Sleep(time.Millisecond * 50)
        }
        wg.Wait() /*等待协程执行完毕*/
    }

    7.多个协程中的消息传递: channel管道 for range循环管道的时候必须要关闭管道 close(ch)
        ch := make(chan int, 3) # 创建管道 可读可写
        ch := make(chan<- int,3) # 只写管道 
        ch := make(<-chan int,3) # 只读管道
        ch <- 11 # 管道写入数据
        num := <-ch # 管道取出数据

    8. select 多路复用 不需要关闭通道

21. 反射: 
    reflect.TypeOf(x) # 获取x的数据类型
    reflect.ValueOf(x) # 获取x的值

22. 框架beego
    go get github.com/astaxie/beego # 下载beego
    bee new beegodemo01 # 创建项目
    bee run # 启动运行项目

 

posted @ 2023-04-24 17:30  龍飛鳯舞  阅读(12)  评论(0编辑  收藏  举报