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 = `\r\n`

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 个数据\n", 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\n", 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 个协程准备开始... \n", i)

            lock.RLock()

            fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁\n", 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\n", 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

 

posted @ 2021-01-06 16:16  蓝辰进击者  阅读(362)  评论(0编辑  收藏  举报