20个Golang片段让我不再健忘

打印 上一主题 下一主题

主题 1980|帖子 1980|积分 5950

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
前言

本文使用代码片段的形式来解释在 go 语言开发中经常遇到的小功能点,由于本人主要使用 java 开发,因此会与其作比较,希望对大家有所帮助。
1. hello world

新手村的第一课,毋庸置疑。
  1. package main
  2. import "fmt"
  3. func main() {
  4.         fmt.Printf("hello world")
  5. }
复制代码
2. 隐形初始化
  1. package main
  2. import "fmt"
  3. func main() {
  4.         load()
  5. }
  6. func load() {
  7.         fmt.Printf("初始化..手动%s 不错\n", "1")
  8. }
  9. func init() {
  10.         fmt.Printf("隐形初始化。。\n")
  11. }
复制代码
在 go 中定义 init 函数,程序在运行时会自动执行。类似使 junit 的 [@before](https://my.oschina.net/u/3870904) 注解。
3. 多模块的访问

java 中 package 包的概念,go 是通过文件夹 + package 关键字来定义的。
一般而言,我们会通过go init来创建项目,生成的go.mod文件位于根目录。
常见的实践是,创建文件夹并且保持 package 名称与文件夹保持一致。这样 import 的永远是文件夹,遵循以上规则则意味着文件夹的名称即为模块名。
同一个 package 可以创建多个 .go 文件,虽然分布在不同的文件中。但是他们中的方法名称不能相同。需要注意,这里与 java中不同类中方法可以重名不同。
此外,也没有诸如private、protected、public等包访问权限关键字。只要定义的函数首字母为大写。则可以被外部成功调用。
来看一下示例:
  1. go-tour
  2. └── ch3
  3.     ├── model
  4.     │   └── test
  5.     │   │   ├── testNest.go
  6.     │   └── helper.go
  7.     │   └── helper2.go
  8.     │  
  9.     └── main.go           
  10.     └── go.mod
复制代码
此处,ch3、model、test 均为文件夹,也可以说是 package。helper.go 位于 model 下,它的代码如下:
  1. package model
  2. import "fmt"
  3. var AppName = "bot"
  4. var appVersion = "1.0.0"
  5. func Say() {
  6.         fmt.Printf("%s", "hello")
  7. }
  8. func init() {
  9.         fmt.Printf("%s,%s", AppName, appVersion)
  10. }
复制代码
再来看看 main.go
  1. package main
  2. import (
  3.         "ch3/model"
  4.         "ch3/model/test"
  5. )
  6. func main() {
  7.         model.Say()
  8. }
复制代码
显然它的调用是通过 packageName.MethodName() 来使用的。需要注意的是,一个 go.mod 下只能有一个 main 包。
4. 引用外部库

和 java 的 maven 类似,go 几经波折也提供了官方仓库。如下,通过 go get github.com/satori/go.uuid 命令即可安装 uuid 库,未指定版本,因此下载的为最新版本。
使用时是这样的:
  1. package main
  2. import (
  3.         "fmt"
  4.         uuid "github.com/satori/go.uuid"
  5. )
  6. func main() {
  7.         uuid := uuid.NewV4()
  8.         fmt.Printf("%s", uuid)
  9. }
复制代码
5. 数组字典和循环

直接看代码就是了。
  1. package main
  2. import "fmt"
  3. var item []int
  4. var m = map[int]int{
  5.         100: 1000,
  6. }
  7. var m2 = make(map[int]int)
  8. func main() {
  9.         for i := 0; i < 10; i++ {
  10.                 item = append(item, i)
  11.                 m[i] = i
  12.                 m2[i] = i
  13.         }
  14.         for i := range item {
  15.                 fmt.Printf("item vlaue=%d\n", i)
  16.         }
  17.         for key, value := range m {
  18.                 fmt.Printf("m:key=%d,value=%d\n", key, value)
  19.         }
  20.         for _, value := range m2 {
  21.                 fmt.Printf("m2:value=%d\n", value)
  22.         }
  23. }
复制代码

  • := 的形式只能在方法内
  • 全局的只能用 var x=..
  • map输出没有顺序
6. 结构体和JSON

go 中通过 struct 来定义结构体,你可以把它简单理解为对象。一般长这样。
  1. type App struct {
  2.         AppName    string
  3.         AppVersion string `json:"app_version"`
  4.         appAuthor  string "pleuvoir"
  5.         DefaultD   string "default"
  6. }
复制代码
我们经常在 java 程序中使用 fastjson 来输出 JSON字符串。 go 中自带了这样的类库。
  1. package main
  2. import (
  3.         app2 "app/app" //可以定义别名
  4.         "encoding/json"
  5.         "fmt"
  6. )
  7. func main() {
  8.         a := app2.App{}
  9.         fmt.Printf("%s\n", a)
  10.         app := app2.App{AppName: "bot", AppVersion: "1.0.1"}
  11.         json, _ := json.Marshal(app) //转换为字符串
  12.         fmt.Printf("json is %s\n", json)
  13. }
复制代码

  • 结构体中 JSON 序列化不会转变大小写,可以指定它输出的 key名称通过 json:xxx 的描述标签。
  • 结构体中的默认值赋值了也不展示
7. 异常处理

作为一个有经验的程序员:),go 的异常处理涉及的很简单,也往往为人所诟病。比如满屏幕的 err 使用。
  1. package main
  2. import (
  3.         "fmt"
  4.         "os"
  5. )
  6. func _readFile() (int, error) {
  7.         file, err := os.ReadFile("test.txt")
  8.         if err != nil {
  9.                 fmt.Printf("error is = %s\n", err)
  10.                 return 0, err
  11.         }
  12.         fmt.Printf("file = %s \n", file)
  13.         return len(file), err
  14. }
  15. func readFile() (int, error) {
  16.         fileLength, err := _readFile()
  17.         if err != nil {
  18.                 fmt.Printf("异常,存在错误 %s\n", err)
  19.         }
  20.         return fileLength, err
  21. }
  22. func main() {
  23.         fileLength, _ := readFile()
  24.         fmt.Printf("%d\n", fileLength)
  25. }
复制代码
和 java 不同,它支持多返回值,为我们的使用带来了很多便利。如果不需要处理这个异常,可以使用 _ 忽略。
8. 异步

千呼万唤始出来,令人兴奋的异步。
  1. package main
  2. import (
  3.         "bufio"
  4.         "fmt"
  5.         "os"
  6. )
  7. func worker() {
  8.         for i := 0; i < 10; i++ {
  9.                 fmt.Printf("i=%d\n", i)
  10.         }
  11. }
  12. func main() {
  13.         go worker()
  14.         go worker()
  15.         //阻塞 获取控制台的输出
  16.         reader := bufio.NewReader(os.Stdin)
  17.         read, err := reader.ReadBytes('\n') //注意是单引号 回车后结束控制台输出
  18.         if err != nil {
  19.                 fmt.Printf("err is =%s\n", err)
  20.                 return
  21.         }
  22.         fmt.Printf("read is %s \n", read)
  23. }
复制代码
如此的优雅,如此的简单。只需要一个关键字 go 便可以启动一个协程。我们在 java 中经常使用的是线程池,而在 go 中也存在协程池。据我观察,部分协程池 benchmark 的性能确实比官方语言关键字高很多。
9. 异步等待

这里就类似 java 中使用 countdownLatch 等关键字空值并发编程中程序的等待问题。
  1. package main
  2. import (
  3.         "fmt"
  4.         "sync"
  5.         "time"
  6. )
  7. func upload(waitGroup *sync.WaitGroup) {
  8.         for i := 0; i < 5; i++ {
  9.                 fmt.Printf("正在上传 i=%d \n", i)
  10.         }
  11.         time.Sleep(5 * time.Second)
  12.         waitGroup.Done()
  13. }
  14. func saveToDb() {
  15.         fmt.Printf("保存到数据库中\n")
  16.         time.Sleep(3 * time.Second)
  17. }
  18. func main() {
  19.         begin := time.Now()
  20.         fmt.Printf("程序开始 %s \n", begin.Format(time.RFC850))
  21.         waitGroup := sync.WaitGroup{}
  22.         waitGroup.Add(1)
  23.         go upload(&waitGroup)
  24.         go saveToDb()
  25.         waitGroup.Wait()
  26.         fmt.Printf("程序结束 耗时 %d ms ", time.Now().UnixMilli()-begin.UnixMilli())
  27. }
复制代码
sync 包类似于 J.U.C 包,里面可以找到很多并发编程的工具类。sync.WaitGroup 便可以简简单单认为是 countdownLatch 吧。也不能多次调用变为负数,否则会报错。
注意,这里需要传入指针,因为它不是一个引用类型。一定要通过指针传值,不然进程会进入死锁状态。
10. 管道

[code]package mainimport (        "fmt"        "sync")var ch = make(chan int)var sum = 0 //是线程安全的func consumer(wg *sync.WaitGroup) {        for {                select {                case num, ok :=
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

篮之新喜

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表