一个计算机技术爱好者与学习者

0%

好好学Golang:Golang入门篇

1. Golang简介

Golang,也称为Go,是由Google开发的一种静态强类型、编译型的系统级编程语言,具有语法简洁、并发支持、高效的内存管理和垃圾回收机制等特性。它由Ken Thompson、Rob Pike和Robert Griesemer于2007年设计,旨在解决当时Google面临的软件开发与维护问题。

Golang 语言语法与 C 语言相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。

本文中,我们开始学习Golang,主要参考:

2. 为什么选择Golang

Golang的优势:

  • 简洁且高效的编程语言:Go的语法设计非常简洁,上手快,编译速度快,性能接近C/C++。
  • 并发编程:Go的并发是它的核心特性之一。使用Go协程(goroutines)和通道(channels)可以轻松编写并发应用程序。
  • 丰富的标准库:Go拥有广泛的标准库,涵盖网络编程、加密、数据处理等各方面,大部分日常功能无需外部依赖。
  • 跨平台编译:Go支持跨平台编译,可以方便地交叉编译,在不同的操作系统和架构上运行。

3. 安装配置Golang环境

3.1. 通用安装配置方法

1、访问Go官网,下载安装Golang环境。

2、把Golang的bin目录添加到Path环境变量。

3、访问Jetbrains-Go,下载安装Goland。

4、在Goland中配置好GOROOT和GOPATH。

3.2. MacOS安装配置Golang环境

1、安装Golang
方法一:使用 brew 安装

1
brew install golang

默认安装到 /usr/local/Cellar/go/ 目录。

方法二(推荐):下载安装包安装
访问Go官网,下载安装包,双击安装。

默认安装到 /usr/local/go/ 目录。

2、配置环境变量
.bash_profile 中添加环境变量:

1
2
3
4
5
# golang
export GOROOT="/usr/local/go"
export GOPATH="$HOME/git/go"
export GOBIN="$HOME/git/go/bin"
export PATH="$PATH:$GOROOT/bin:$GOPATH/bin"

4. helloworld

按照国际惯例,学习新语言,先来一个helloworld。新建test.go文件,内容为:

1
2
3
4
5
6
7
package main

import "fmt"

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

Shift+右键打开Powershell,使用go run test.go,运行代码。
除了使用go run命令之外,还可以使用go build命令,先对代码进行编译,然后运行生成的二进制文件。

由上面这段代码可以看出,Go语言代码的基础组成有包声明、引入包、函数、语句和表达式。除此之外,还有注释、常量、变量、数组、结构体等。

5. Golang基础语法

5.1. 程序结构

Go程序的基本组织单元是包(packages)。每一个Go程序都是由一个或多个包组成的。main 包是每个可执行程序的入口点,程序从main函数开始运行。

5.2. 变量与数据类型

Go是静态类型的语言,每个变量都有明确的类型。Go提供的基本类型包括boolstring,数值类型如intfloat64等,以及byterune

声明变量的一般格式是:

1
var variableName dataType = value

类型推导(使用:=运算符)允许你在声明变量时不显式指定数据类型,而是由编译器推导类型。

1
name := "Go Programming"

5.3. 函数

函数是Go中的一类对象。函数的定义包括函数名、参数列表、返回值列表和函数体。

1
2
3
func functionName(parameterName dataType) returnType {
    // 函数体
}

5.4. 指针

Go支持指针,意味着可以直接通过引用来读写内存地址中的数据。指针在Go中比在许多其他语言中使用起来更安全,因为Go隐藏了指针算术。

1
2
var x int = 1
var p *int = &x // p 是指向 x 的指针

5.5. 控制结构

Go语言支持常见的控制结构,如ifelsefor(用作循环,因为Go中没有while语句)、switchselect

1
2
3
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

5.6. 结构体和接口

结构体(structs)是定义复合数据类型的方式,而接口(interfaces)定义了对象的行为。在Go中,接口是隐式实现的,任何类型只要实现了接口中的方法即被视为实现了该接口。

1
2
3
4
5
6
7
8
9
10
11
type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

5.7. 并发

Go的并发模型基于协程,称为goroutines,它们相对于线程来说更轻量级。channels用于协程之间的通信和同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func count(thing string, c chan string) {
    for i := 1; i <= 5; i++ {
        c <- thing
    }
    close(c)
}

func main() {
    c := make(chan string)
    go count("sheep", c)
    for msg := range c {
        fmt.Println(msg)
    }
}

6. Golang和C语法对比

Golang的语法和C语言很像,这里和C进行对比,指出不同之处,方便记忆。

  • {不能单独放在一行
  • 表达式不需要分号结尾
  • 变量声明赋值,var age int = 0
  • 数据类型有差异
  • 强制类型转换时括号的位置不同
  • 支持多变量赋值,按顺序赋值
  • :=操作符也可以声明变量,但是只能在函数体内
  • const声明可以使用iota
  • if和for后面不需要圆括号
  • switch不需要break
  • 循环都用for,没有while
  • 函数定义,func func_name([params]) [return_types]{}
  • 数组声明,var arr [10] float32; var arr2 = [3]float32{1.0, 2.0, 3.4}
  • 结构体定义,type先行,下面有demo
  • 切片(动态数组),var slice1 []int = make([]int, 3, 9)
  • 切片append,copy
  • 字典map定义,var map1 map[string]string = make(map[string]string)
  • 字典delete条目
  • for range循环切片和字典
  • 定义接口,下面有demo
  • 错误处理,error接口,下面有demo
  • 并发,go语句开启新线程,下面有demo

7. Golang命名规则

文件名:Go语言的文件名通常是全小写,最好不要使用下划线和中划线,非要使用的话选下划线。例如,http_server.go。这种命名方式也是Go语言社区的普遍约定俗成的规则。
Go测试文件中,是有特殊的命名规则的:如果你的文件名为abc.go,那么对应的测试文件应该命名为abc_test.go。
同时,单元测试文件通常与被测试文件放在同一个目录,具体做法可以参考beego项目。这是Go标准库中普遍采用的方式,go test测试工具可以找到并运行这些测试文件。

包名:Go中的包名应当使用小写字母。如果包名由多个单词组成,那么一般不使用下划线或者中划线,而是直接将小写字母连在一起,例如httputil,ioutil等。

变量名、函数名、常量名:变量名和函数名通常使用驼峰命名法,当标识符的首字母大写时,其作用域为公共,可以被其他包访问;当首字母小写时,其作用域为包内私有。例如 ServerAddr 是公共的,而 serverAddr 是私有的。

结构体字段: 结构体的字段名也遵循驼峰命名法,并且一般会首字母大写以使其可以被其他包访问。

接口名:单个方法的接口应以“er”为后缀。例如 io.Reader、io.Writer等。

错误类型:在命名错误类型时,以 “Error” 结尾。

在Go语言中通常避免使用下划线,特别是在包命名,结构体,函数和变量命名中。一些特殊的情况,例如测试和协议缓冲区,可能会使用下划线。

8. Golang代码示例

8.1. 结构体demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import "fmt"

type Books struct {
title string
author string
subject string
book_id int
}

func main() {

// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.voidking.com", "Go 语言教程", 6495407})

// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.voidking.com", subject: "Go 语言教程", book_id: 6495407})

// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.voidking.com"})

// 结构体声明和赋值
var Book1 Books

Book1.title = "Go语言入门篇"
Book1.author = "www.voidking.com"
Book1.subject = "Go语言"
Book1.book_id = 123456

printBook(Book1)
}

func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title);
fmt.Printf( "Book author : %s\n", book.author);
fmt.Printf( "Book subject : %s\n", book.subject);
fmt.Printf( "Book book_id : %d\n", book.book_id);
}

8.2. 接口demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"fmt"
)

type Phone interface {
call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}

func main() {
var phone Phone

phone = new(NokiaPhone)
phone.call()

phone = new(IPhone)
phone.call()

}

8.3. 错误处理demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
"fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}

// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}

}

func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当被除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}

8.4. 并发demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"time"
)

func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}

func main() {
say("hello")
go say("world")
}

8.5. 单元测试demo

1、准备项目

1
2
3
mkdir test-demo && cd test-demo
go mod init test-demo
mkdir pkg1

2、创建 pkg1/hello_test.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package pkg1

import (
"fmt"
"testing"
)

func TestHello(t *testing.T) {
fmt.Println("Hello, World!")
}

func TestHello2(t *testing.T) {
fmt.Println("Hello, World! Again!")
}

3、执行单元测试

1
2
3
4
5
6
7
8
9
# 查找单个文件,执行匹配到正则表达式的函数
go test -v pkg1/hello_test.go -run TestHello
go test -v pkg1/hello_test.go -run TestHello2

# 执行单个文件中的所有函数
go test -v pkg1/hello_test.go

# 查找整个包,执行匹配到正则表达式的函数
go test -v test-demo/pkg1 -run TestHello
  • 本文作者: 好好学习的郝
  • 原文链接: https://www.voidking.com/dev-golang-start/
  • 版权声明: 本文采用 BY-NC-SA 许可协议,转载请注明出处!源站会即时更新知识点并修正错误,欢迎访问~
  • 微信公众号同步更新,欢迎关注~