博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
5 分钟内学习 Go 语言?
阅读量:2289 次
发布时间:2019-05-09

本文共 6184 字,大约阅读时间需要 20 分钟。

点击上方蓝色“Golang来啦”关注我哟

加个“星标”,天天 15 分钟,掌握 Go 语言

via:

https://gist.github.com/prologic/5f6afe9c1b98016ca278f4d507e65510
作者:prologic
四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!

上周五的分享预告点赞已破百,成功解锁了今天的文章。

前几天看到优质文章推送,注意到这篇文章(主要是标题吸引了我),文章非常简洁,让我们一起来回顾下相关的基础知识。

原文如下:


基本

作为经典的 “Hello World”,你的第一个 Go 程序非常简单:

先为我们的项目创建 workspace:

$ mkdir hello$ cd hello

接着创建并初始化 Go module:

$ go mod init hello

使用你最喜欢的编辑器创建 main.go 文件,输入如下代码,并将文件保存在上面创建的 hello 目录下。

package mainimport "fmt"func main() {  fmt.Println("Hello World!")}

最后编译产生一个二进制文件:

$ go build

在 workspace 下会生成一个名为 hello 的二进制可执行文件,运行它会输出:

$ ./helloHello World!

变量

在 Go 语言里,你可以使用下面两种方法创建变量:

var x int

其他类型,包括 int、int32、int64、float32、float64、bool和 string 等,还有带 u 前缀的无符号整型类型,例如:uint8 与 byte 是同一种类型(ps:可以认为 type 是 uint8 的别称,见源码 type byte = uint8)

或者创建变量时直接赋值,Go 编译器能自动推导变量类型:

x := 42

通过操作符 = 赋值:

x = 1

注意: 大部分时候你都不需要使用 var 关键字,除非创建变量之后再赋值或者需要使用变量的零值。

函数

使用关键字 func 声明函数:

func hello(name string) string {  return fmt.Sprintf("Hello %s", name)}

函数如果有返回值,必须显示地指明返回值类型。

函数可以有多个返回值(一般返回错误和值):

func isEven(n int) (bool, error) {  if n <= 0 {    return false, fmt.Errorf("error: n must be > 0")  }  return n % 2 == 0, nil}

函数是 Go 语言的“一等公民”,因此支持很多函数式编程的很多特性,包括闭包、将函数作为参数传递或者返回等,例如:

func AddN(n int) func(x int) int {  return func(x int) int {    return x + n  }}

结构体

由于 Go 是一种多范式语言,因此它还通过结构体实现“面向对象”编程,使用关键字 struct 定义结构体:

type Account struct {  Id      int  Balance float64}

结构体字段的定义与变量定义类似,使用操作符 . 访问字段:

account := Account{}fmt.Printf("Balance: $%0.2f", account.Balance)

方法

结构体可以拥有自己的方法。不像其他语言,Go 语言不支持继承也没有类(我们可以通过结构体嵌套实现“继承”)。

type Account struct {  id  int  bal float64}func (a *Account) String() string {  return fmt.Sprintf("Account[%d]: $0.2f", a.id, a.bal)}func (a *Account) Deposit(amt flaot64) float64 {  a.bal += amt  return a.bal}func (a *Account) Withdraw(amt float64) float64 {  a.bal -= amt  return a.bal}func (a *Account) Balance() float64 {  return a.bal}

这些方法称为指针接收者方法,因为第一个参数是指向 Account 结构体的指针。

你还可以像下面这样定义方法:

type Circle struct {  Radius float64}func (c Circle) Area() float64 {  return 3.14 * c.Radius * c.Radius}

刚刚定义的是值接收者方法,在方法中对结构体修改不会影响到原结构体,是一份拷贝。

关于结构体、指针接收者方法和值接收者方法可以查看文末推荐阅读了解更多。

数组和 Slice

与其他语言类似,Go 支持数组,不同的是 Go 语言数组是固定长度的(声明长度之后不支持动态变化),与 C 语言数组类似。

可以指定大小和类型创建数组:

xs := [4]int{1, 2, 3, 4}

大多数场景你会更喜欢使用 slice,有点类似其他语言(比如 Python)中的 list,支持自动扩容。

创建 slice 时可以不用指定大小:

xs := []int{1, 2, 3, 4}

slice 也可以通过 append() 函数创建:

xs := []int{}xs = append(xs, 1)xs = append(xs, 2)xs = append(xs, 3)

可以通过索引范围 slice:

xs[1]  // 2

你还可以通过“切片”操作获取数组或者 slice 的子集:

ys := xs[1:] // [2, 3]

可以使用 range 关键字迭代 数组或者 slice:

for i, x := range xs {  fmt.Printf("xs[%d] = %d\n", i, x)}

map

Go 语言里存储键值对的内置数据结构称为 map(类似其他语言的 hash table、hash map、字典或者关联数组)。

可以通过关键字 map 创建 map,需要分别定义键值的类型,比如定义键类型为 string、值类型为 int 的 map:

var counts map[string]int

也可以像下面这样创建 map:

counts := map[string]int{  "Apples": 4,  "Oranges": 7,}

类似数组和 slice,也可以通过键访问值:

counts["Apples"]  // 4

像数组和 slice 一样,迭代 map:

for key, value := range counts {  fmt.Printf("%s: %d\n", key, value)}

唯一需要注意的是,map 使用之前需要先初始化,操作 nil map 将会导致 error 和 panic:

var counts map[string]intcounts["Apples"] = 7  // This will cause an error and panic!

使用之前必须使用 make() 函数初始化:

counts := make(map[string]int)counts["Apples"] = 7

关于数组、slice 和 map 更详细的用法可以查看文末推荐阅读掌握。

流控制

for

Go 语言里面只有一种循环结构,在之前的章节里或许你也看到了:

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

for 循环语句由三部分组成:

  • 初始化语句:第一次迭代之前执行;

  • 条件表达式:每次迭代之前都会执行;

  • post 语句:每次迭代之后都会执行;

如果忽略循环条件,得到的是无限循环:

for {}// This line is never reached!

if-else

Go 语言支持 if-else 语句:

N := 42func Guess(n int) string {  if n == 42 {    return "You got it!"  } else if n < N {    return "Too low! Try again..."  } else {    return "Too high! Try again..."  }}        

注意:可以忽略最后一个 else,直接像这样返回 return "Too high~ Try again…",两者是等价的。

switch

Go 语言提供的 switch 语句可以代替 if-else,比如:

func FizzBuzz(n int) string {  switch n {  case n % 15 == 0:    return "FizzBuzz"  case n % 3 == 0:    return "Fizz"  case n % 5 == 0:    return "Buzz"  default:    return fmt.Sprintf("%d", n)  }}

defer

关键字 defer 可以定义延迟函数,在调用函数返回之前调用,通常在调用函数执行结束之前自动关闭资源(比如文件句柄、数据库连接等),像下面这样:

package mainimport (  "os"  "fmt")func Goodbye(name string) {  fmt.Printf("Goodbye %s", name)}func Hello(name string) {  defer Goodbye(name)  fmt.Printf("Hello %s", name)}func main() {  user := os.Getenv("User")  Hello(user)}

执行会输出:

$ ./helloHello prologicGoodbye prologic

关于 defer 语句的详细使用方法可以阅读文末推荐阅读掌握。

错误处理

Go 语言里 Errors 代表的是值,比如,使用 os.Open 打开一个文件,如果成功的话会返回一个指向文件的指针和 nil;否则返回 nil 指针和具体的错误。

f, err := os.Open("/path/to/file")

类似其他值类型一样检查错误:

f, err := os.Open("/path/to/file")if err == nil {  // do something with f}

Go 语言里,通常的做法是检查函数是否返回非 nil 错误,是的话就直接返回,比如:

func AppendFile(fn, text string) error {  f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.WR_ONLY, 0644)  if err != nil {    return fmt.Errorf("error opening file for writing: %w", err)  }  defer f.Close()  if _, err := f.Write([]byte(text)); err != nil {    return fmt.Errorf("error writing text to fiel: %w", err)  }  return nil}

创建和导入包

Go 支持 module(包管理),允许你创建并导入相关包,在刚开始的基本章节中,我们已经知道如何在启动新项目时使用 go mod init 创建模块。

Go 的包仅仅是一个包含源代码目录:

创建 Go 包的第一步是创建一个目录,就像下面这样:

$ mkdir shapes

使用命令 go mod init 初始化:

$ cd shapes$ go mod init github.com/prologic/shapes

现在让我们一起使用你最喜欢的编辑器创建一个 circle.go 模块:

package shapestype Circle struct {  Radius float64}func (c Circle) String() string {  return fmt.Sprintf("Circle(%0.2f)", c.Radius)}func (c Circle) Area() float64 {  return 3.14 * c.Radius * c.Radius}

需要特别注意的是,包里的函数、结构体、变量和常量的首字母必须是大写才是可导出的;否则即使你导入了相关包也无法访问。

现在,在 Git 上创建名为 shapes 的仓库并 push 你的包代码:

$ git init$ git commit -a -m "Initial Commit"$ git remote add origin git@github.com:prologic/shapes.git$ git push -u origin master

这样的话,你就可以使用全路径 github.com/prologic/shapes 导入 shapes 包,编译时会根据路径自动拉取并构建包。

例如:

我们使用 github.com/prologic/shapes 包做一个例子:

$ mkdir hello$ go mod init hello

编写 main.go:

package mainimport (  "fmt"  "github.com/prologic/shapes")func main() {  c := shapes.Circle{Radius: 5}  fmt.Printf("Area of %s: %0.2f\n", c, c.Area())}

编译:

$ go build

运行二进制文件:

$ ./helloArea of Circle(5.00): 78.50

Congratulations!

Now you're a Gopher!

ps: 看原文评论,作者反馈接下来还会继续更新原文,我们拭目以待,如果更新的话第一时间给大家分享。

推荐阅读:

结构体、方法:

数组、map 和 slice:

defer:


这是持续翻译的第 13/100 篇优质文章。

如果你有想交流的话题,欢迎留言。


如果我的文章对你有所帮助,点赞、转发都是一种支持!

给个[在看],是对四哥最大的支持

转载地址:http://syfnb.baihongyu.com/

你可能感兴趣的文章
最全的大厂最新面试249题与笔记总结:多线程+JVM +Spring+微服务+分布式+Redis+MySQL
查看>>
吊!设计模式全解:6大设计原则+23种设计模式+设计模式PK+设计模式混编
查看>>
服!看完阿里大牛手写的Java异步编程实战笔记,我惊呆了
查看>>
Java程序员跳槽,三面之后被拒,原因竟是“招不起”
查看>>
想要彻底搞懂微服务架构?必先学:SpringBoot+SpringCloud+docker
查看>>
6天面试10家,已经拿到offer,Java程序员的面试总结分享
查看>>
渣本的逆袭之路!备战3个月,三面蚂蚁金服成功斩获Offer
查看>>
10月末美团、滴滴、蘑菇街9次面试总结(Java岗)
查看>>
热气腾腾的腾讯后台开发面经(总共五面)
查看>>
普本开发三年,每天两小时面试备战,2个月后五面阿里定级P7
查看>>
深入理解设计模式(设计原则+种设计模式+设计模式PK+设计模式混编)
查看>>
谷歌大佬回国发展,吊打各大厂面试官!吐血总结大厂面试高频点及笔记解析
查看>>
面试复盘:面完字节、美团、阿里等大厂,今年面试到底问什么?
查看>>
从0到1,决战Spring Boot《Spring Boot 2实战之旅》
查看>>
5面终于拿到字节跳动offer!忍不住和大家分享一波
查看>>
拿到阿里、字节offer后。我总结了一线大厂Java面试重难点:Java基础+并发+JVM+算法+框架+分布式+架构设计
查看>>
金九银十已过 成功入职美团,面试回顾及个人总结:算法+框架+Redis+分布式+JVM
查看>>
香!阿里P8手写3份满级“并发编程”笔记,原理→精通→实战
查看>>
大专Java开发,美团被裁,腾讯不要,奋发复习五面阿里斩获offer定级P7
查看>>
五面美团后,我总结出美团面试四大难题:JVM+微服务+MySQL+Redis
查看>>