马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
前言
本文使用代码片段的形式来解释在 go 语言开发中经常遇到的小功能点,由于本人主要使用 java 开发,因此会与其作比较,希望对大家有所帮助。
1. hello world
新手村的第一课,毋庸置疑。- package main
- import "fmt"
- func main() {
- fmt.Printf("hello world")
- }
复制代码 2. 隐形初始化
- package main
- import "fmt"
- func main() {
- load()
- }
- func load() {
- fmt.Printf("初始化..手动%s 不错\n", "1")
- }
- func init() {
- fmt.Printf("隐形初始化。。\n")
- }
复制代码 在 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等包访问权限关键字。只要定义的函数首字母为大写。则可以被外部成功调用。
来看一下示例:- go-tour
- └── ch3
- ├── model
- │ └── test
- │ │ ├── testNest.go
- │ └── helper.go
- │ └── helper2.go
- │
- └── main.go
- └── go.mod
复制代码 此处,ch3、model、test 均为文件夹,也可以说是 package。helper.go 位于 model 下,它的代码如下:- package model
- import "fmt"
- var AppName = "bot"
- var appVersion = "1.0.0"
- func Say() {
- fmt.Printf("%s", "hello")
- }
- func init() {
- fmt.Printf("%s,%s", AppName, appVersion)
- }
复制代码 再来看看 main.go- package main
- import (
- "ch3/model"
- "ch3/model/test"
- )
- func main() {
- model.Say()
- }
复制代码 显然它的调用是通过 packageName.MethodName() 来使用的。需要注意的是,一个 go.mod 下只能有一个 main 包。
4. 引用外部库
和 java 的 maven 类似,go 几经波折也提供了官方仓库。如下,通过 go get github.com/satori/go.uuid 命令即可安装 uuid 库,未指定版本,因此下载的为最新版本。
使用时是这样的:- package main
- import (
- "fmt"
- uuid "github.com/satori/go.uuid"
- )
- func main() {
- uuid := uuid.NewV4()
- fmt.Printf("%s", uuid)
- }
复制代码 5. 数组字典和循环
直接看代码就是了。- package main
- import "fmt"
- var item []int
- var m = map[int]int{
- 100: 1000,
- }
- var m2 = make(map[int]int)
- func main() {
- for i := 0; i < 10; i++ {
- item = append(item, i)
- m[i] = i
- m2[i] = i
- }
- for i := range item {
- fmt.Printf("item vlaue=%d\n", i)
- }
- for key, value := range m {
- fmt.Printf("m:key=%d,value=%d\n", key, value)
- }
- for _, value := range m2 {
- fmt.Printf("m2:value=%d\n", value)
- }
- }
复制代码
- := 的形式只能在方法内
- 全局的只能用 var x=..
- map输出没有顺序
6. 结构体和JSON
go 中通过 struct 来定义结构体,你可以把它简单理解为对象。一般长这样。- type App struct {
- AppName string
- AppVersion string `json:"app_version"`
- appAuthor string "pleuvoir"
- DefaultD string "default"
- }
复制代码 我们经常在 java 程序中使用 fastjson 来输出 JSON字符串。 go 中自带了这样的类库。- package main
- import (
- app2 "app/app" //可以定义别名
- "encoding/json"
- "fmt"
- )
- func main() {
- a := app2.App{}
- fmt.Printf("%s\n", a)
- app := app2.App{AppName: "bot", AppVersion: "1.0.1"}
- json, _ := json.Marshal(app) //转换为字符串
- fmt.Printf("json is %s\n", json)
- }
复制代码
- 结构体中 JSON 序列化不会转变大小写,可以指定它输出的 key名称通过 json:xxx 的描述标签。
- 结构体中的默认值赋值了也不展示
7. 异常处理
作为一个有经验的程序员:),go 的异常处理涉及的很简单,也往往为人所诟病。比如满屏幕的 err 使用。- package main
- import (
- "fmt"
- "os"
- )
- func _readFile() (int, error) {
- file, err := os.ReadFile("test.txt")
- if err != nil {
- fmt.Printf("error is = %s\n", err)
- return 0, err
- }
- fmt.Printf("file = %s \n", file)
- return len(file), err
- }
- func readFile() (int, error) {
- fileLength, err := _readFile()
- if err != nil {
- fmt.Printf("异常,存在错误 %s\n", err)
- }
- return fileLength, err
- }
- func main() {
- fileLength, _ := readFile()
- fmt.Printf("%d\n", fileLength)
- }
复制代码 和 java 不同,它支持多返回值,为我们的使用带来了很多便利。如果不需要处理这个异常,可以使用 _ 忽略。
8. 异步
千呼万唤始出来,令人兴奋的异步。- package main
- import (
- "bufio"
- "fmt"
- "os"
- )
- func worker() {
- for i := 0; i < 10; i++ {
- fmt.Printf("i=%d\n", i)
- }
- }
- func main() {
- go worker()
- go worker()
- //阻塞 获取控制台的输出
- reader := bufio.NewReader(os.Stdin)
- read, err := reader.ReadBytes('\n') //注意是单引号 回车后结束控制台输出
- if err != nil {
- fmt.Printf("err is =%s\n", err)
- return
- }
- fmt.Printf("read is %s \n", read)
- }
复制代码 如此的优雅,如此的简单。只需要一个关键字 go 便可以启动一个协程。我们在 java 中经常使用的是线程池,而在 go 中也存在协程池。据我观察,部分协程池 benchmark 的性能确实比官方语言关键字高很多。
9. 异步等待
这里就类似 java 中使用 countdownLatch 等关键字空值并发编程中程序的等待问题。- package main
- import (
- "fmt"
- "sync"
- "time"
- )
- func upload(waitGroup *sync.WaitGroup) {
- for i := 0; i < 5; i++ {
- fmt.Printf("正在上传 i=%d \n", i)
- }
- time.Sleep(5 * time.Second)
- waitGroup.Done()
- }
- func saveToDb() {
- fmt.Printf("保存到数据库中\n")
- time.Sleep(3 * time.Second)
- }
- func main() {
- begin := time.Now()
- fmt.Printf("程序开始 %s \n", begin.Format(time.RFC850))
- waitGroup := sync.WaitGroup{}
- waitGroup.Add(1)
- go upload(&waitGroup)
- go saveToDb()
- waitGroup.Wait()
- fmt.Printf("程序结束 耗时 %d ms ", time.Now().UnixMilli()-begin.UnixMilli())
- }
复制代码 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 := |