Go语言入门指南 | 青训营

145 阅读4分钟

1 走进 Go 语言

1.1 Go 语言简介

什么是 Go 语言?

Go语言(也称为Golang)是一种开源的静态类型编程语言,由Google于2007年开始开发,并于2009年首次公开发布。它的设计目标是将程序员的生产力和计算机的性能相结合,以提供高效、可靠和简洁的软件开发体验。

Go 语言的优点

  • 简洁易学:Go语言的语法简单清晰,具有较少的关键字和语法结构,易于学习和阅读。它强调代码的可读性和简洁性,减少样板代码的编写。
  • 并发支持:Go语言原生支持并发编程,它通过轻量级的Go协程(goroutine)和通信顺序进程(CSP)模型来实现。这使得编写高效的并发程序变得更加简单和直观。
  • 内存安全:Go语言具有内置的垃圾回收机制,可以自动管理内存。它还提供了严格的类型安全性和边界检查,以避免常见的内存错误,例如空指针引用和缓冲区溢出。
  • 快速编译:Go语言的编译速度非常快,可以在几秒钟内将大型程序编译成可执行文件。这对于开发迭代和构建大规模应用程序非常有利。
  • 跨平台支持:Go语言具有很好的跨平台性,可以在多个操作系统上进行开发和部署,包括Windows、Linux和macOS等。
  • 强大的标准库:Go语言附带了一个丰富而强大的标准库,涵盖了网络编程、文件处理、加密解密、测试、并发等各个领域。这些库提供了一致性、高质量和高性能的解决方案。
  • 社区活跃:Go语言拥有一个活跃的开源社区,提供了许多优秀的第三方库和工具。开发者可以从中获取各种资源和支持,并与其他开发者交流和分享经验。

1.2 Go 语言环境安装

下载地址 goproxy.cn/ go.dev/

下载对应操作系统的安装包,根据教程安装即可

配置集成开发环境

可以根据自己的喜好选择集成开发环境,GoLand 或者 VS Code 都可以,自行搜索安装配置教程,这里不再赘述

1.3 体验 Go 语言

安装好 Go 开发环境之后,我们来感受一下 Go 语言

来到 Go 语言的世界,发出第一声呐喊吧!

package main

import (
	"fmt"
)

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

2 Go 语言基本语法

2.1 Go 语言变量类型

先看一个例子

package main

import (
	"fmt"
	"math"
)

func main() {
	var a = "initial"

	var b, c int = 1, 2

	var d = true

	var e float64

	f := float32(e)

	g := a + "foo"

	fmt.Println(a, b, c, d, e, f)
	fmt.Println(g)

	const s string = "constant"
	const h = 5000000000
	const i = 3e20 / h
	fmt.Printf(s, h, i, math.Sin(h), math.Sin(i))

}

执行结果

Go 语言声明变量的方式和 C 语言类似

变量可以通过var关键字或者:=声明,变量声明时可以指定类型,也可以自动推断类型

常量用const关键字声明,其用法和var类似

Go 语言中的变量类型

在Go语言中,变量类型包括以下几种:

  • 布尔类型(bool):表示真(true)或假(false)的值。
  • 整数类型:包括有符号整数(int、int8、int16、int32、int64)和无符号整数(uint、uint8、uint16、uint32、uint64)等。
  • 浮点数类型:包括单精度浮点数(float32)和双精度浮点数(float64)。
  • 复数类型:包括复数的实部和虚部都是浮点数的类型(complex64、complex128)。
  • 字符串类型:用于表示文本或字符序列的类型(string)。
  • 数组类型:表示具有相同数据类型的固定长度序列的类型。
  • 切片类型(slice):表示可变长序列的类型,是对数组的抽象。
  • 映射类型(map):表示键值对的集合类型。
  • 结构体类型(struct):表示自定义的复合数据类型,可以包含多个字段,与C语言类似。
  • 接口类型(interface):表示一组方法的集合,用于实现多态性。
  • 函数类型(func):表示函数的类型,可以作为参数或返回值。

在Go语言中,变量需要显式地声明其类型,可以使用关键字var进行声明和初始化,也可以使用短变量声明:=进行快速的声明和初始化操作。

2.2 Go 语言中的判断语句与循环语句

  1. if 语句

和 C 语言不同的是,if 后的条件部分不用写括号

package main

import (
	"fmt"
)

func main() {
	a := 1

	if a > 0 {
		fmt.Println(a)
	} else {
		fmt.Println(a + 1)
	}
}
  1. 循环语句

Go 语言只有 for 循环,无while、do...while 循环等,其用法与 C 语言类似

func main() {
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
}
  1. switch...case...开关语句,用法和 C 语言类似
func main() {
	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	default:
		fmt.Println("other")
	}
}

2.3 Go 语言中的数组和切片

Go 语言中的数组和 C 语言中的数组类似,下面简单介绍一下基本用法

  • 创建一个可以容纳 5 个元素的 int 类型数组
var a [5]int
  • 创建数组并且给数组赋初始值
	a := [5]int{1, 2, 3, 4, 5}
  • 创建多维数组
var twoD [2][3]int

twoD := [2][3]int

切片类型

在go语言中,切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集;切片的内存分布是连续的,所以可以把切片当做一个大小不固定的数组。切片有三个字段的数据结构:指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)。

func main() {
	// 用 make 关键字创建一个长度为 3 的字符类型切片
	s := make([]string, 3)
	// 给切片赋值
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	//用 append 关键字切片长度拓展
	s = append(s, "d", "e")

	// 切片拷贝
	c := make([]string, len(s))
	copy(c, s)
	fmt.Println(c) // [a,b,c,d,e]

	// 取值,类似 python
	fmt.Println(s[0]) //[a]
	fmt.Println(s[1:3]) //[b,c,d]
	fmt.Println(s[:4])  //[a,b,c,d]
}

切片类型用法和特点类似 python 语言中的列表、Java 语言中的 List

2.4 Go 语言中的 Map 类型

在 Java 和 C++ 语言中都有 map 类型,Go 语言也不例外

我们可以用 make 来创健一个空 map,golang 的 map 是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。

Go 语言中 map 的简单用法如下:

func main() {
	// 创建一个 map key 和 value 都是 string 类型
	m := map[string]string{"a": "A", "b": "B", "c": "C"}

	// 遍历 map 键值对
	for k, v := range m {
		fmt.Println(k, v)
	}
	
	// 遍历 map 的 key
	for k := range m {
		fmt.Println(k)
	}
}

2.5 Go 语言中的指针类型

和 C 语言用法类似

2.6 Go 语言中的函数类型

Golang 和其他很多语言不一样的是,变量类型是后置的

Golang 里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是错误信息

package main

import "fmt"

// 返回两个数的和
func add(a int, b int) int {
	return a + b
}

// 判断 map 中是否存在对应值,返回值和判断结果
func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
    // 调用函数计算两个数相加
	res := add(1, 2)
	fmt.Println(res)

    // 调用函数判断 map 中是否存在 key 为 “a” 的键值对
	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok)
}

2.7 Go 语言中的 struct(结构体) 类型

  • 类似 Java 语言中的类,用 type 和 struct 关键字定义一个结构体
  • 可以给结构体添加结构体方法,类似其他语言中的类成员方法
  • 当需要对结构体中的值做修改,函数中需要传入指针类型
package main

import (
	"fmt"
)

// 定义一个结构体
type user struct {
	name     string
	password string
}

// 结构体方法
func (u user) checkPassword(password string) bool {
	return u.password == password
}

func main() {
	// 实例化结构体
	a := user{name: "wang", password: "1024"}
	b := user{"zhang", "1234"}
	var c user
	c.password = "1234"
	c.name = "liu"

	fmt.Println(a, b)
	fmt.Println(a.checkPassword("1111"))

}

2.8 Go 语言错误处理

  • 错误处理在Go语言里面符合语言习惯的做法就是使用一个单独的返回值来传递错误信息
  • 不同于Java使用的异常。go语言的处理方式,能够很清地知道哪个函数返回了错误,并且能用简单的if else来处理错误。
  • 在函数里面,我们可以在函数的返回值类型里面加一个error,就代表这个函数可能会返回错误。
  • 在函数实现的时候,return需要同时return两个值,如果出现错误的话,那么可以return nil和一个error。.如果没有的话,那么返回原本的结果和nil。
package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("Not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name)

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(u.name)
	}

}

2.9 Go 中的 JSON 操作

  • go语言里面的 JSON 操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写。那么这个结构体就能用JSON.marshaler去序列化,变成一个JSON的字符串。
  • 序列化,之后的字符串也能够用JSON.unmarshaler去反序列化到一个空的变量里面。
import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "Typescript"}}

	buf, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","Typescript"]}

	buf, err = json.MarshalIndent(a, "", "\t")

	if err != nil {
		panic(err)
	}

	fmt.Println(string(buf))

	var b userInfo
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", b) //main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "Typescript"}}

}

2.10 Go 时间处理

  • 时间处理在go语言里面最常用的就是 time.now() 来获取当前时间
  • 也能用Sub去对两个时间进行减法,得到一个时间段。
  • 在和某些系统交互的时候,我们经常会用到时间戳,可以用 UNIX() 来获取时间戳。
import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now) // 2023-07-26 17:05:21.7990381 +0800 CST m=+0.003261001

	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)
	fmt.Println(t.Format("2002-01-30 15:04:12"))
	diff := t2.Sub(t)
	fmt.Println(diff)
	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)
	fmt.Println(now.Unix())

2.11 Go 数字字符串解析

  • 在go语言当中,关于字符串和数字类型之间的转换都在strconv这个包下,这个包是string convert这两个单词的缩写。
  • 我们可以用 parselnt 或者 parseFloat 来解析一个字符串。可以用Atoi把一个十进制字符串转成数字。可以用itoA把数字转成字符串。如果输入不合法,那么这些函数都会返回error
import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("1.235", 64)
	fmt.Println(f) // 1.235

	n, _ := strconv.ParseInt("123", 10, 64)
	fmt.Println(n) // 123

	n2, _ := strconv.Atoi("123")
	fmt.Println(n2) //123

	n3 := strconv.Itoa(65)
	fmt.Println(n3 + "432")  // 65432

}

参考资料

www.php.cn/faq/499329.…