美文网首页
Go学习笔记(一)

Go学习笔记(一)

作者: dev_winner | 来源:发表于2019-08-03 21:29 被阅读0次
    • 是一门编译型语言,运行效率高,开发高效,部署简单;语言层面支持并发,易于利用多核实现并发;内置runtime(作用:性能监控,GC等);简单易学,丰富的标准库,强大的网络库;内置强大的工具(gofmt),跨平台编译,内嵌C支持。
    • 应用:服务器编程,分布式系统 ,网络编程,云平台(docker)
    • 命令行工具:
      1、go build:用于编译源码文件、代码包、依赖包;
      2、go run:可以编译并运行Go源码文件;
      3、go get:命令主要用动态获取远程代码包。
    Go保留的关键字只有25个 Go中36个预定标识符(包括基本数据类型和系统内嵌函数)
    // 单行注释
    /* 多行注释 */
    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        "fmt"
        // "time"  // 导入的包要使用,否则会产生编译错误
    )
    
    // 定义全局常量
    const NAME string = "global const var"
    
    // 定义全局变量
    var tmp string = "global var"
    
    // 一般类型声明
    type countInt int
    
    // 结构体的声明
    type Learn struct {
    
    }
    
    // 声明接口
    type Ilearn interface {
    
    }
    
    // 函数定义
    func learnGo() {
        fmt.Println("learn go!")
    }
    
    // 主函数
    func main() {
        fmt.Println("Hello World!!!")
        fmt.Println(NAME)
        fmt.Println(tmp)
        learnGo()
    }
    
    • package是基本的分发单位和工程管理中依赖关系的体现。
    • 每个Go语言源代码文件的开头都拥有一个package声明,表示源码文件所属代码包;要生成Go语言可执行程序,必须要有main的package包,且必须在该包下有main()函数
    • 同一个路径下只能存在一个package,一个package可以拆分成多个源文件组成。
    • import语句可以导入源代码文件所依赖的package包
    • 导入的包要使用,否则会产生编译错误!
    • 若导入的包中依赖其他包(包B),会首先导入包B,然后初始化包B中的常量和变量,最后若包B中有init()函数,会自动执行init()函数,所有包导入完成后才会对main中的常量和变量进行初始化,然后执行main中的init()函数(如果存在),最后才执行main函数。
    • 若有一个包被导入多次,则该包只会被导入一次。
    • 函数名首字母要大写
    • 转义字符:\r表示回车,从当前行的最前面开始输出,覆盖掉旧的内容
    • 格式化go源代码文件:执行命令:gofmt -w 文件名.go;或者先按组合键shift+tab将代码整体移动到最左边,再选择某些代码块按tab键适当缩进即可。
    • 行长约定:一行最长不超过80个字符,超过则用逗号隔开并换行展示!

    错误记录

    • 作为一个go的初学者,首先必须了解一个go项目的目录结构,例如我的一个测试目录如下:
    workspace 
             |--bin
    
             |--pkg
    
             |--src
                   |---learn
                            |----learn.go
                   |---show
                            |----show.go
             |--main.go
    
    • bin目录存放编译后的可执行文件pkg目录存放编译后的包文件(名称以.a为后缀的文件)src目录存放项目源文件
    • 一般地,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
    • 注意:某个.go文件import导入包的过程是先去$GOROOT下的src目录找对应的包,找到则导入;若找不到,则去存放Go的项目路径$GOPATH下的src目录找对应的包,找到则导入;若还找不到,则报错!
    • 刚开始还不知道go项目的目录结构时,我在main.go文件中导入learn和show包是这样的:
    import (
        "fmt"
        "src/learn"
        "src/show"
    )
    

    结果报如下错误,细细观察,通过查找和对比,可以发现上面讲的原理;

    import包名的查找顺序

    于是,把main.go文件中import包的代码改一下(去掉"src"):

    import (
        "fmt"
        "learn"
        "show"
    )
    

    再次键入执行命令:go run main.go,结果就成功执行了!

    执行结果

    learn.go的代码如下:

    package learn
    
    import (
        "fmt"
        "show"
    )
    
    func init() {
        show.Show()
        fmt.Println("learn init...")
    }
    
    func Learn() {
        fmt.Println("learn 学习...")
    }
    

    show.go的代码如下:

    package show
    
    import (
        "fmt"
    )
    
    func init() {   
        fmt.Println("show init...")
    }
    
    func Show() {
        fmt.Println("show 展示...")
    }
    

    main.go的代码如下:

    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        "fmt"
        // "time"  // 导入的包要使用,否则会产生编译错误
        "learn"
        "show"
    )
    // 函数定义
    func learnGo() {
        fmt.Println("learn go!")
    }
    // init函数
    func init() {
        fmt.Println("main init...")
    }
    // 主函数
    func main() {
        show.Show()
        learn.Learn()
        learnGo()
    }
    
    • 键入go env可以查看go的配置:例如我的环境配置如下:
    go的环境配置
    • import别名、._
      1、import别名:
    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        zzz "fmt"  // 包"fmt"的别名定义为zzz
        // "time"  // 导入的包要使用,否则会产生编译错误
        "learn"
        "show"
    )
    
    // 函数定义
    func learnGo() {
        zzz.Println("learn go!")
    }
    
    // init函数
    func init() {
        zzz.Println("main init...")
    }
    
    // 主函数
    func main() {
        show.Show()
        learn.Learn()
        learnGo()
    }
    

    2、点(.)操作的含义是:点(.)表示的包导入后,调用该包中函数时可以省略前缀包名;

    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        . "fmt"  // 使用 . 操作,调用包"fmt"中的函数时,省略包名.
        // "time"  // 导入的包要使用,否则会产生编译错误
        "learn"
        "show"
    )
    
    // 函数定义
    func learnGo() {
        Println("learn go!")  // 省略包名fmt.
    }
    
    // init函数
    func init() {
        Println("main init...")
    }
    
    // 主函数
    func main() {
        show.Show()
        learn.Learn()
        learnGo()
    }
    

    3、下划线(_)操作的含义是:导入该包,但不导入整个包,而是执行该包的init函数,因此无法通过包名来调用包中的其他函数。使用下划线(_)操作往往是为了注册包里的引擎,让外部可以方便地使用。

    • 代码包初始化函数即无参数声明和结果声明的init函数。
    • init函数可以被声明在任何文件中,且可以有多个。
    (_)测试1 执行产生错误

    以上报错说明无法调用learn.go文件中的Learn()函数,我们将main()函数中的代码learn.Learn()注释掉,再用命令行执行main.go可以发现执行了learn.go中的init()函数,说明_操作只调用init()函数,当然在执行init()函数之前,会先初始化learn.go文件中的常量和变量。

    • init函数的执行时机:单一代码包内,对所有全局变量进行求值,然后执行所有init函数,同一个代码包中多个init函数的执行顺序是不确定。

    Go语言的基本数据类型

    1、布尔型:只可以是常量 true 或者 false,不能是其他类型。一个简单的例子:var b bool = true。
    2、整数类型:整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
    1.uint8:无符号 8 位整型 (0 到 255)
    2.uint16:无符号 16 位整型 (0 到 65535)
    3.uint32:无符号 32 位整型 (0 到 4294967295)
    4.uint64:无符号 64 位整型 (0 到 18446744073709551615)
    5.int8:有符号 8 位整型 (-128 到 127)
    6.int16:有符号 16 位整型 (-32768 到 32767)
    7.int32:有符号 32 位整型 (-2147483648 到 2147483647)
    8.int64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
    3、浮点数类型
    1.float32:IEEE-754 32位浮点型数
    2.float64:IEEE-754 64位浮点型数
    3.complex64:32 位实数和虚数
    4.complex128:64 位实数和虚数
    4、字符串类型string,编码统一为"UTF-8"。
    5、其他数字类型
    1.byte:类似 uint8,无符号类型。
    2.rune:类似 int32,表示一个Unicode码。
    3.uint:32 或 64 位,其取决于操作系统的位数
    4.int:与 uint 一样大小,其取决于操作系统的位数
    5.uintptr:无符号整型,用于存放一个指针

    • 查看某个变量所占的字节数:unsafe.Sizeof(变量),导入import "unsafe"包!

    • 派生类型
      1、指针类型(Pointer)
      2、数组类型
      3、结构化类型(struct)
      4、Channel类型(chan)
      5、函数类型(func)
      6、切片类型(slice)
      7、接口类型(interface)
      8、Map类型(map)

    • 类型零值和类型别名
      1、类型零值不是空值,而是某个变量被声明后的默认值,一般情况下,值类型的默认值为0,布尔类型默认值为false,string默认值为空字符串。
      2、类型别名:type 别名 = 数据类型,例如:type D = int
      3、类型声明:type 别名 数据类型,例如:type d int
      4、区别:类型声明将d定义为一个新的数据类型,该类型拥有和int一样的特性,但是两者是不同类型不能进行算术等运算!而类型别名则将D定义为int整型的一个别名,使用D和int相同。二者可以当作同一种运算。别名只在源码中存在,编译完成后,不会有别名类型
    • 查看某个变量的类型:reflect.TypeOf(变量名),导入import "reflect"包。

    变量与常量

    • 单个变量的声明和赋值
      1、变量的声明格式:var <变量名称> [变量类型]
      2、变量的赋值格式:<变量名称> = <值,表达式,函数等>
      3、声明和赋值同时进行:var <变量名称> [变量类型] = <值,表达式,函数等>
      4、分组声明格式:
    var(
      a int
      b float32
      name string
    )
    

    5、同一行声明多个变量和赋值:var a, b, c int = 1, 2, 3 或者a, b := 1, 2。注意::=是声明并赋值;系统会自动推断类型;不需要var关键字;只能使用在局部变量。
    6、全局变量必须使用var关键字,局部变量则可以省略。
    7、特殊变量下划线_:相当于一个垃圾桶,把值赋值给下划线
    ,相当于把值丢进一个垃圾桶,以后都不再使用。
    8、Go语言中不存在隐式转换,类型转换必须是显式转换。类型转换只能发生在两种兼容类型之间;类型转换格式:<变量名称> [:]= <目标类型>(<需要转换的变量>)

    • 变量的可见性规则
      1、大写字母开头的变量可导出的,也就是其他包可以读取的,是公共变量
      2、小写字母开头的变量就是不可导出的,是私有变量。即导入的某个包,只能使用该包中的公有变量,不能使用私有变量。
    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        . "fmt"
        "reflect"
    )
    
    var(
        a uint
        b float32
        c bool
        d string = "你好"
    )
    
    func main() {
        var v1, v2, v3 = 4, 5, 1.222  // 可以声明每个变量的类型,省略的话--->系统自动推断类型
        Println(reflect.TypeOf(v3))  // float64
        Println(v1, v2, v3)  // 4 5 1.222
        Println(a, b, c, d) // 0 0 false 你好
    
        c1, c2, c3 := 33, "string", 1.222 // 简写方式,系统自动推断类型,只能用在函数体内
        Println(reflect.TypeOf(c2)) // string
        Println(c1, c2, c3) // 33 string 1.222
    
        // 下划线的使用
        // var a1, _, a2 = 9, 8, 7
        // Println(a1, _, a2) // 报错:
        // # command-line-arguments
        // .\main.go:31:9: cannot use _ as value
    
        // 类型转换测试: 只能用在相同类型之间进行转换
        var h float64 = 2.6555555
        k := int64(h)  // 括号不能省
        Println(k) // 2,直接向下取整
        Println(reflect.TypeOf(k))  // int64
    }
    
    • 常量定义从形式上分为显式和隐式:
      1、显式:const identifier [type] = value
      2、隐式:const identifier = value (通常叫无类型常量)
    • 常量可以使用内置表达式定义:例如:len()unsafe.Sizeof()
      3、常量范围目前只支持布尔型,数字型(整数型、浮点型和复数)和字符串型。
    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        . "fmt"
        "reflect"
    )
    
    const(
        name string = "cat" // 显式定义
        age = 12 // 隐式定义
    )
    
    const a, b, c = 1, 2, "我是中文"
    
    const lenc = len(c)  // 一个中文占三个字节,长度为12
    
    func main() {
        Println(name, age)  // cat 12
        Println(a, b, c) // 1 2 我是中文
        Println(reflect.TypeOf(c)) // string
        Println(lenc) // 12
    }
    
    • 特殊常量iota的使用
      1、iota在const关键字出现时将被重置为0;
      2、const中每新增一行常量声明将使iota计数一次。
      3、iota常见用法:
      1.跳值使用法;
      2.插队使用法;
      3.表达式隐式使用法;
      4.单行使用法。
    // 程序所属包,每个.go文件必须要有package关键字
    package main // 包名尽量与文件目录名一样
    
    // 导入依赖包
    import (
        . "fmt"
    )
    
    // iota在关键字出现时被重置为0
    const a = iota // 0
    const b = iota // 0
    
    const(
        c = iota
        m = 2014 // 插入使用法,iota依然计数
        d = iota
        _  // 跳值使用法,下划线单独占用一行,iota依然计数
        e = iota
    )
    
    const(
        b1 = iota * 3
        b2 // 自动使用向上第一个非空表达式,此时每增加一行常量const,iota计数加1,即b2 = 1 * 2 
        b3 // b3 = 2 * 3 = 6
    )
    
    const(
        c1, c2 = iota + 1, iota * 3
        c3, c4  // 沿用上面第一个非空的表达式,注意格式要一样,且const新增一行,iota计数器才会加1
        c5 = iota // 否则就得赋予值
    
    )
    
    func main() {
        Println(a, b) // 0 0
        Println(c, d, e) // 0 2 4
        Println(b1, b2, b3) // 0 3 6
        Println(c1, c2, c3, c4, c5) // 1 0 2 3 2
    }
    

    相关文章

      网友评论

          本文标题:Go学习笔记(一)

          本文链接:https://www.haomeiwen.com/subject/ifjsdctx.html