• golang语法笔记


    开始微服务,那就先温习下golang语法吧;

     

    golang变量类型

    1. 整形

     

    Go

    %b    表示为二进制

    %c    该值对应的unicode码值

    %d    表示为十进制

    %o    表示为八进制

    %q    该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示

    %x    表示为十六进制,使用a-f

    %X    表示为十六进制,使用A-F

    %U    表示为Unicode格式:U+1234,等价于"U+%04X"

    %E    用科学计数法表示

    %f    用浮点数表示

    2. 浮点型

    • • float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度
    • • float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度

    3. Byte rune

     

    Go

    var a byte = 65   byte,占用1个节字

    var b rune = 'B'   rune,占用4个字节

    4. String

    转义:

     

    Go

    var mystr02 string = ` `

    var mystr01 string = "\r\n"

    换行:

    var mystr01 string = `你好呀!

    我的公众号是: Go欢迎大家关注`

    5. Array

     

    Go

    // 第一种方法

    var arr [3]int = [3]int{1,2,3}

    // 第二种方法

    arr := [3]int{1,2,3}

    arr := [...]int{1,2,3}

    6. 切片

    区间: 左闭右开

     

    Apache

    arr[0:2]  --> 1,2

    sliTest := make([]int, 2, 3)

    sliTest[1] = 5

    sliTest[0] = 1

    sliTest = append(sliTest, 6)

    7. Map

     

    Go

    mapA := map[int]string{1:"one",2:"two"}

    mapA[6] = "six"

    //进行判断key是否存在:

    if math, ok := mapA[6]; ok {

    }

    //循环遍历:key+value

    for key,value := range mapA{

       fmt.Println(key,value)

    }

    //循环遍历:key

    for key := range mapA{

       fmt.Println(key)

    }

    //循环遍历:value

    for _, value := range mapA{

       fmt.Println(value)

    }

    8. Ptr

    指针

     

    Go

    ptr := new(string)

    *ptr = "string ptr"

    str := *ptr

    ptr2 := &str

    in1t := 1

    ptr3 := &in1t

    ptr4 := &(*ptr3)

    println("name:",sliTest)

    9. Switch case

     

    Go

    func getResult(args ...int) bool {

        return true

    }

    //接函数

    switch getResult(chinese, english, math) {

        // case 后也必须是布尔类型

        case true:

            fmt.Println("该同学所有成绩都合格")

        case false:

            fmt.Println("该同学有挂科记录")

    }

    //这种 可以替代if else

    score := 30

    switch {

        case score >= 95 && score <= 100:

            fmt.Println("优秀")

        case score >= 80:

            fmt.Println("良好")

        case score >= 60:

            fmt.Println("合格")

        case score >= 0:

            fmt.Println("不合格")

        default:

            fmt.Println("输入有误...")

    }

    //穿透性

    s := "hello"

    switch {

        case s == "hello":

            fmt.Println("hello")

            fallthrough //直接不比较就穿透到下面的case,不进行break

        case s != "world":

            fmt.Println("world")

        }

    output: hello world

    10. For

    for [condition |  ( init; condition; increment ) | Range]{

       statement(s);

    }

    • • condition: 接一个条件表达式

     

    Go

    a := 1

    for a <= 5 {

        fmt.Println(a)

        a ++

    }

    • • ( init; condition; increment ): 接三个表达式

     

    Go

    for i := 1; i <= 5; i++ {

        fmt.Println(i)

    }

    • • Range: 接一个 range 表达式

     

    Go

    for key, value := range myarr {

        fmt.Println(key,value)

    }

    • • 不接表达式

     

    Go

    for {

        fmt.Println("无限")

    }

    // 等价于

    for ;; {

        fmt.Println("无限")

    }

    11. Goto

     

    Go

    if name == "lern" {

          goto flagEnd

       }

       goto flag

       //var i = 0  这个会报错,flag间不允许定义

    flag:

       fmt.Println("flag")

       fmt.Println("flag2")

       fmt.Println("flag3")

    flagEnd:

       fmt.Println("flagEnd")

    12. Defer

     

    Go

    func myfunc() {

        fmt.Println("B")

    }

    func main() {

        defer myfunc()

        fmt.Println("A")

    }

    输出:

    A

    B

    //先后顺序:  遵循栈的方式

    func main() {

        name := "go"

        defer fmt.Println(name) // 输出: go

        name = "python"

        defer fmt.Println(name) // 输出: python

        name = "java"

        fmt.Println(name)

    }

    输出:

    java

    python

    go

    13. interface{}

     

    Go

    print("string")

    func print(param interface{}){

        if value, ok := param.(string); ok{

            fmt.Println(value)      

        }

    }

    14. Select case

    • • select 只能用于 channel 的操作(写入/读出),而 switch 则更通用一些;
    • • select 的 case 是随机的,而 switch 里的 case 是顺序执行;
    • • select 要注意避免出现死锁,同时也可以自行实现超时机制;
    • • select 里没有类似 switch 里的 fallthrough 的用法;
    • • select 不能像 switch 一样接函数或其他表达式。

     

    Go

    func main() {

        c1 := make(chan string, 1)

        c2 := make(chan string, 1)

        c2 <- "hello"

        select {

        case msg1 := <-c1:

          fmt.Println("c1 received: ", msg1)

        case msg2 := <-c2:

          fmt.Println("c2 received: ", msg2)

        default:

          fmt.Println("No data received.")

        }

    }

    15. panic

    异常,可以采用recover来进行恢复

     

    Go

    func set_data(x int) {

        defer func() {

            // recover() 可以将捕获到的panic信息打印

            if err := recover(); err != nil {

                fmt.Println(err)

            }

        }()

        // 故意制造数组越界,触发 panic

        var arr [10]int

        arr[x] = 88

    }

    func main() {

        set_data(20)

        // 如果能执行到这句,说明panic被捕获了

        // 后续的程序能继续运行

        fmt.Println("everything is ok")

    }

    输出:

    runtime error: index out of range [20] with length 10

    everything is ok

    golang面向对象

    1. Struct

     

    Go

    //基类

    type base struct {

       name string

    }

    type userinfo struct {

       name string

       age int

       base  //组合

    }

    func(self *userinfo) dump() {

       fmt.Println(self.name) //输出的是qingfeng

       fmt.Println(self.age)

    }

    func main()  {

       bb := base{name: "qqq"}

       user := userinfo{

          name: "qingfeng",

          age:  19,

          base: bb,

       }

       user.dump()

       fmt.Println(user.base.name) //这个才输出 qqq

    }

    2. Interface

     

    Go

    type caller interface {

       test1()

       test2()

    }

    type son struct {

       userid int

       username string

    }

    type mun struct {

       userid int

       username string

    }

    func (self son) test1()  {

       fmt.Println("son_test1",self.userid)

    }

    func (self son) test2()  {

       fmt.Println("son_test2",self.username)

    }

    func (self mun) test1()  {

       fmt.Println("mun_test1",self.userid)

    }

    func (self mun) test2()  {

       fmt.Println("mun_test2",self.username)

    }

    m_son := son{

       userid:   1001,

       username: "1001",

    }

    m_mun := mun{

       userid:   1005,

       username: "1005",

    }

    m_calls := []caller{m_son, m_mun}

    for _,v := range m_calls{

       v.test1()

       v.test2()

    }

    3. x.()

     

    Go

    func findType(i interface{}) {

        switch x := i.(type) {

        case int:

            fmt.Println(x, "is int")

        case string:

            fmt.Println(x, "is string")

        case nil:

            fmt.Println(x, "is nil")

        default:

            fmt.Println(x, "not type matched")

        }

    }


    var k interface{} // nil

    t3, ok := k.(interface{})

    fmt.Println(t3, "-", ok)

    var k int // nil

    t3, ok := k.(int)

    fmt.Println(t3, "-", ok)

    4. reflect

     

    Go

    func main() {

        var name string = "Go编程时光"

        fmt.Println("真实世界里 name 的原始值为:", name)

        v1 := reflect.ValueOf(&name)

        v2 := v1.Elem()

        v2.SetString("Python编程时光")

        fmt.Println("通过反射对象进行更新后,真实世界里 name 变为:", name)

    }

    内容太多:参考文章,写的挺详细的

    http://golang.iswbm.com/en/latest/c02/c02_08.html

    5. Func

     

    Go

    func double(a int) (b int) {

        // 不能使用 := ,因为在返回值哪里已经声明了为int

         b = a * 2

        // 不需要指明写回哪个变量,在返回值类型那里已经指定了

         return

     }

     

     func main() {

         fmt.Println(double(2))

    }

    output: 4

    6. chan

     

    Go

    func main() {

        pipline := make(chan int, 10)

        fmt.Printf("信道可缓冲 %d 个数据 ", cap(pipline))

        pipline<- 1

        fmt.Printf("信道中当前有 %d 个数据", len(pipline))

    }

    定义只读信道

     

    Go

    var pipline = make(chan int)

    type Receiver = <-chan int // 关键代码:定义别名类型

    var receiver Receiver = pipline

    定义只写信道

     

    Go

    var pipline = make(chan int)

    type Sender = chan<- int  // 关键代码:定义别名类型

    var sender Sender = pipline

    仔细观察,区别在于 <- 符号在关键字 chan 的左边还是右边。

    • • <-chan 表示这个信道,只能从里发出数据,对于程序来说就是只读
    • • chan<- 表示这个信道,只能从外面接收数据,对于程序来说就是只写

    chan用来做锁

     

    Go

    // 由于 x=x+1 不是原子操作// 所以应避免多个协程对x进行操作// 使用容量为1的信道可以达到锁的效果

    func increment(ch chan bool, x *int) {

        ch <- true

        *x = *x + 1

        <- ch

        }

    func main() {

        // 注意要设置容量为 1 的缓冲信道

        pipline := make(chan bool, 1)

        var x int

        for i:=0;i<1000;i++{

            go increment(pipline, &x)

        }

        // 确保所有的协程都已完成

        // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep

        time.Sleep(3)

        fmt.Println("x 的值:", x)

        }

     

    7. WaitGroup

    等待协程结束

    • • Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量
    • • Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。
    • • Wait:阻塞当前协程,直到实例里的计数器归零。

     

    Go

    func worker(x int, wg *sync.WaitGroup) {

        defer wg.Done()

        for i := 0; i < 5; i++ {

            fmt.Printf("worker %d: %d ", x, i)

        }

    }

    func main() {

        var wg sync.WaitGroup

        wg.Add(2)

        go worker(1, &wg)

        go worker(2, &wg)

        wg.Wait()

    }

    8. Mutex

    互斥锁:



    Go

    func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) {

        for i := 0; i < 1000; i++ {

            lock.Lock() //加锁

            *count = *count + 1

            lock.Unlock() //解锁

        }

        wg.Done()

    }

    func main() {

        var wg sync.WaitGroup

        lock := &sync.Mutex{}

        count := 0

        wg.Add(3)

        go add(&count, &wg, lock)

        go add(&count, &wg, lock)

        go add(&count, &wg, lock)

        wg.Wait()

        fmt.Println("count 的值为:", count)

    }

    读写锁:

     

    Go

    func main() {

        lock := &sync.RWMutex{}

        lock.Lock()

        for i := 0; i < 4; i++ {

            go func(i int) {

                fmt.Printf("第 %d 个协程准备开始... ", i)

                lock.RLock()

                fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁 ", i)

                time.Sleep(time.Second)

                lock.RUnlock()

            }(i)

        }

        time.Sleep(time.Second * 2)

        fmt.Println("准备释放写锁,读锁不再阻塞")

        // 写锁一释放,读锁就自由了

        lock.Unlock()

        // 由于会等到读锁全部释放,才能获得写锁

        // 因为这里一定会在上面 4 个协程全部完成才能往下走

        lock.Lock()

        fmt.Println("程序退出...")

        lock.Unlock()

    }

    9. goroutine

    go的协程,理解成语言级别的

     

    Go

    func mygo(name string) {

        for i := 0; i < 10; i++ {

            fmt.Printf("In goroutine %s ", name)

            // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠

            time.Sleep(10 * time.Millisecond)

        }

    }

    func main() {

        go mygo("协程1号") // 第一个协程

        go mygo("协程2号") // 第二个协程

        time.Sleep(time.Second)

    }

    golang环境:

    1. Go mod

    • • go mod init:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。
    • • go mod download:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod目录)
    • • go mod graph: 打印项目的模块依赖结构
    • • go mod tidy :添加缺少的包,且删除无用的包
    • • go mod verify :校验模块是否被篡改过
    • • go mod why: 查看为什么需要依赖
    • • go mod vendor :导出项目所有依赖到vendor下
    • • go mod edit :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 go help mod edit
    • • go list -m -json all:以 json 的方式打印依赖详情

    golang编码规范:

    1. 文件命名

    • • 由于 Windows平台文件名不区分大小写,所以文件名应一律使用小写
    • • 不同单词之间用下划线分词,不要使用驼峰式命名
    • • 如果是测试文件,可以以 _test.go 结尾
    • • 文件若具有平台特性,应以 文件名_平台.go 命名,比如 utils_ windows.go,utils_linux.go,可用的平台有:windows, unix, posix, plan9, darwin, bsd, linux, freebsd, nacl, netbsd, openbsd, solaris, dragonfly, bsd, notbsd, android,stubs
    • • 一般情况下应用的主入口应为 main.go,或者以应用的全小写形式命名。比如MyBlog 的入口可以为 myblog.go

    2. 常量命名

    目前在网络上可以看到主要有两种风格的写法

    • • 第一种是驼峰命名法,比如 appVersion
    • • 第二种使用全大写且用下划线分词,比如 APP_VERSION

    这两种风格,没有孰好孰弱,可自由选取,我个人更倾向于使用第二种,主要是能一眼与变量区分开来。

    如果要定义多个变量,请使用 括号 来组织。

     

    Go

    const (

        APP_VERSION = "0.1.0"

      CONF_PATH = "/etc/xx.conf")

    3. 变量命名

    和常量不同,变量的命名,开发者们的喜好就比较一致了,统一使用 驼峰命名法

    • • 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u
    • • 若该变量为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头。例如:isExist ,hasConflict 。
    • • 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。
    • • 若变量中有特有名词(以下列出),且变量为私有,则首单词还是使用全小写,如 apiClient。
    • • 若变量中有特有名词(以下列出),但变量不是私有,那首单词就要变成全大写。例如:APIClient,URLString

    这里列举了一些常见的特有名词:

     

    Go

    // A GonicMapper that contains a list of common initialisms taken from golang/lintvar LintGonicMapper = GonicMapper{

        "API":   true,

        "ASCII": true,

        "CPU":   true,

        "CSS":   true,

        "DNS":   true,

        "EOF":   true,

        "GUID":  true,

        "HTML":  true,

        "HTTP":  true,

        "HTTPS": true,

        "ID":    true,

        "IP":    true,

        "JSON":  true,

        "LHS":   true,

        "QPS":   true,

        "RAM":   true,

        "RHS":   true,

        "RPC":   true,

        "SLA":   true,

        "SMTP":  true,

        "SSH":   true,

        "TLS":   true,

        "TTL":   true,

        "UI":    true,

        "UID":   true,

        "UUID":  true,

        "URI":   true,

        "URL":   true,

        "UTF8":  true,

        "VM":    true,

        "XML":   true,

        "XSRF":  true,

        "XSS":   true,}

    4. 函数命名

    • • 函数名还是使用 驼峰命名法
    • • 但是有一点需要注意,在 Golang 中是用大小写来控制函数的可见性,因此当你需要在包外访问,请使用 大写字母开头
    • • 当你不需要在包外访问,请使用小写字母开头
    • • 另外,函数内部的参数的排列顺序也有几点原则
    • • 参数的重要程度越高,应排在越前面
    • • 简单的类型应优先复杂类型
    • • 尽可能将同种类型的参数放在相邻位置,则只需写一次类型

    5. 接口命名

    使用驼峰命名法,可以用 type alias 来定义大写开头的 type 给包外访问。

     

    Go

    type helloWorld interface {

        func Hello();

        }

    type SayHello helloWorld

    当你的接口只有一个函数时,接口名通常会以 er 为后缀

    type Reader interface {

        Read(p []byte) (n int, err error)

        }

    6. 注释规范

    注释分为

    6.1 代码注释

    用于解释代码逻辑,可以有两种写法

    单行注释使用 // ,多行注释使用 /* comment */

     

    Go

    // 单行注释

    /*多行注释*/

    另外,对于代码注释还有一些更加苛刻的要求,这个看个人了,摘自网络:

    • • 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。
    • • 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。
    • • 包、函数、方法和类型的注释说明都是一个完整的句子。
    • • 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。
    • • 注释的单行长度不能超过 80 个字符。
    • • 类型的定义一般都以单数形式描述:

     

    Go

    // Request represents a request to run a command.  type Request struct { ...

    • • 如果为接口,则一般以以下形式描述:

     

    Go

    // FileInfo is the interface that describes a file and is returned by Stat and Lstat.type FileInfo interface { ...

    • • 函数与方法的注释需以函数或方法的名称作为开头:

     

    Go

    // Post returns *BeegoHttpRequest with POST method.

    • • 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述:

     

    Go

    // Copy copies file from source to target path.// It returns false and error when error occurs in underlying function calls.

    • • 若函数或方法为判断类型(返回值主要为 bool 类型),则以 <name> returns true if 开头:

     

    Go

    // HasPrefix returns true if name has any string in given slice as prefix.func HasPrefix(name string, prefixes []string) bool { ...

    6.2 特别注释

    • • TODO:提醒维护人员此部分代码待完成
    • • FIXME:提醒维护人员此处有BUG待修复
    • • NOTE:维护人员要关注的一些问题说明

    7. 包的导入

    单行的包导入

     

    Go

    import "fmt"

    多个包导入,请使用 {} 来组织

    import {

      "fmt"

      "os"}

    另外根据包的来源,对排版还有一定的要求

    • • 标准库排最前面,第三方包次之、项目内的其它包和当前包的子包排最后,每种分类以一空行分隔。
    • • 尽量不要使用相对路径来导入包。

     

    Go

    import (

        "fmt"

        "html/template"

        "net/http"

        "os"

        "github.com/codegangsta/cli"

        "gopkg.in/macaron.v1"

        "github.com/gogits/git"

        "github.com/gogits/gfm"

        "github.com/gogits/gogs/routers"

        "github.com/gogits/gogs/routers/repo"

        "github.com/gogits/gogs/routers/user"

    )

    8. gofmt

    有空的可以用gofmt来进行格式化;

    参考:https://jingyan.baidu.com/article/c45ad29c64cfe7051653e245.html

     

  • 相关阅读:
    ServerSuperIO 3.5版本的体系结构,以及未来规划的几点思考
    《连载 | 物联网框架ServerSuperIO教程》- 18.集成OPC Client,及使用步骤。附:3.5 发布与更新说明。
    《连载 | 物联网框架ServerSuperIO教程》- 17.集成Golden实时数据库,高并发保存测点数据。附:3.4 发布与版本更新说明。
    《连载 | 物联网框架ServerSuperIO教程》- 16.集成OPC Server,及使用步骤。附:3.3 发布与版本更新说明。
    [祝贺] 东方国信集团的钢铁大数据和工业节能两个案例入选工信部工业互联网优秀案例
    《连载 | 物联网框架ServerSuperIO教程》- 15.数据持久化接口的使用。附:3.2发布与版本更新说明。
    物联网建设中通讯互联层的终极解决方案
    hadoop 2.7.2 + zookeeper 高可用集群部署
    开源物联网框架ServerSuperIO 3.0正式发布(C#),跨平台:Win&Win10 Iot&Ubuntu&Ubuntu Mate,一套设备驱动跨平台挂载,附:开发套件和教程。
    《连载 | 物联网框架ServerSuperIO教程》- 14.配制工具介绍,以及设备驱动、视图驱动、服务实例的挂载
  • 原文地址:https://www.cnblogs.com/left69/p/14241760.html
Copyright © 2020-2023  润新知