Skip to main content

Go Quick Start 快速入门

Go Basics 编程基础

Hello World

先写一段 Go 代码,保存为 hello.go 文件

package main

import (
"fmt"
)

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

在上面程序中,fmt 是 Go 中的内置包,它实现了格式化输入输出的功能。

在 Go 中我们导入一个包使用 import 关键字。func main 是代码执行的入口。Println 是 fmt 包中的一个函数,它可以在控制台打印输出。

# 命令运行 go 程序
go run hello.go

Compile 编译

使用 go build 命令编译

go build hello.go

生成了一个二进制可执行文件 hello

> ./hello
# Hello World!

Visibility Rules 可见性

Go 语言中约定使用 大小写 来决定常量、变量、类型、接口、结构或函数是否可以被外部包所调用

  • 函数名字首字母 小写 即为 private 私有的
  • 函数名字首字母 大写 即为 public 公有

Control structures 控制语句

If

In Go a simple if looks like this:

if x > 0 {
return y
}
package main
import (
"fmt"
"math"
)
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4)) // 1.4142135623730951 2i
}

If 的便捷写法

package main
import (
"fmt"
"math"
)

func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10), // 9
pow(3, 3, 20), // 20
)
}

For

package main

import "fmt"

func main() {
sum := 0
// 如果条件表达式的值变为 false,那么迭代将终止。
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum) // 1+2+3+4+5+6+7+8+9 = 45

// 循环初始化语句和后置语句都是可选的。
// for 是 Go 的 “while”
// 基于此可以省略分号:C 的 while 在 Go 中叫做 for 。
// 如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环。
sum2 := 1
for sum2 < 10 {
sum2 += sum2
}
fmt.Println(sum2) // 1+1+2+4+8 = 16
}

Switch

switch 的执行顺序: 条件从上到下的执行,当匹配成功的时候停止

package main

import (
"fmt"
"time"
)

func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
}

Initialization 初始化

Constants

package main
import "unsafe"
// 常量可以用len(), cap(), unsafe.Sizeof()常量计算表达式的值。
// 常量表达式中,函数必须是内置函数,否则编译不过:
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)

func main(){
const (
PI = 3.14
const1 = "1"
)
const LENGTH int = 10
const e, f, g = 1, false, "str" //多重赋值
println(a, b, c, PI, LENGTH) // abc 3 16 +3.140000e+000 10
}

Variables

var (
name = "gopher"
name1 = "1"
)
// 变量声明
var a int
a = 11

// 变量声明 并赋值
var b int = 12

// 应用在函数体内的方式
var a, b, c, d int = 1, 2, 3, 4
// a =1
// b =2
// c =3
// d =4


var a, _, c, d int = 1, 2, 3, 4
// 忽略 _ 返回值忽略

Type Conversion

// 只能类型显式转换
var a float32 = 1.1
// 省略var, 简短形式,使用 := 赋值操作符
b := int(a)

fmt.Println(b) // 1

// 不兼容的类型不能转换类型

Data 数据类型

Boolean

  • 长度: 1 byte
  • 取值范围: true or false
package main

import "fmt"

func main() {
var b bool
b = true
fmt.Printf("b is of type %t\n", b) // b is of type true
e := bool(true)
fmt.Printf("e is of type %t\n", e) // e is of type true
}

Byte

基本处理函数

  • Contains() 返回是否包含子切片
  • Count() 子切片非重叠实例的数量
  • Map() 函数,将 byte 转化为 Unicode,然后进行替换
  • Repeat() 将切片复制 count,返回这个新的切片
  • Replace() 将切片中的一部分 替换为另外的一部分
  • Runes() 将 S 转化为对应的 UTF-8 编码的字节序列,并且返回对应的`Unicode 切片
  • Join() 函数,将子字节切片连接到一起。
package main

import (
"bytes"
"fmt"
)

func main() {
// 这里不能写成 b := []byte{"Golang"},这里是利用类型转换。
b := []byte("Golang")
subslice1 := []byte("go")
subslice2 := []byte("Go")

fmt.Println(b) // [71 111 108 97 110 103]
fmt.Println(subslice1) // [103 111]

// func Contains(b, subslice [] byte) bool
// 检查字节切片b ,是否包含子字节切片 subslice
fmt.Println(bytes.Contains(b, subslice1))
fmt.Println(bytes.Contains(b, subslice2))

s2 := []byte("同学们,上午好")
m := func(r rune) rune {
if r == '上' {
r = '下'
}
return r
}
fmt.Println(string(s2))
// func Map(mapping func(r rune) rune, s []byte) []byte
// Map函数: 首先将 s 转化为 UTF-8编码的字符序列,
// 然后使用 mapping 将每个Unicode字符映射为对应的字符,
// 最后将结果保存在一个新的字节切片中。
fmt.Println(string(bytes.Map(m, s2)))

s3 := []byte("google")
old := []byte("o")
//这里 new 是一个字节切片,不是关键字了
new := []byte("oo")
n := 1
// func Replace(s, old, new []byte, n int) []byte
//返回字节切片 S 的一个副本, 并且将前n个不重叠的子切片 old 替换为 new,如果n < 0 那么不限制替换的数量
fmt.Println(string(bytes.Replace(s3, old, new, n)))
fmt.Println(string(bytes.Replace(s3, old, new, -1)))

// 将字节切片 转化为对应的 UTF-8编码的字节序列,并且返回对应的 Unicode 切片。
s4 := []byte("中华人民共和国")
r1 := bytes.Runes(s4)
// func Runes(b []byte) []rune
fmt.Println(string(s4), len(s4)) // 字节切片的长度
fmt.Println(string(r1), len(r1)) // rune 切片的长度

// 字节切片 的每个元素,依旧是字节切片。
s5 := [][]byte{
[]byte("你好"),
[]byte("世界"), //这里的逗号,必不可少
}
sep := []byte(",")
// func Join(s [][]byte, sep []byte) []byte
// 用字节切片 sep 吧 s中的每个字节切片连接成一个,并且返回.
fmt.Println(string(bytes.Join(s5, sep)))
}

String

var str string // 声明一个字符串
str = "Go lang" // 赋值
ch := str[0] // 获取第一个字符
len := len(str) // 字符串的长度, len 是内置函数, len = 5

Array

数组是相同数据类型的元素序列。 数组在声明中定义要指定长度,因此不能进行扩展

var a [5]int

数组也可以是多维的

var multiD [2][3]int

当数组的值在运行时不能进行更改。 数组也不能直接获取子数组。所以就有了切片这种类型。

package main

import "fmt"

func main() {
// 声明一个长度为5的整数数组
// 一旦数组被声明了,那么它的数据类型跟长度都不能再被改变。
var array1 [5]int

fmt.Printf("array1: %d\n\n", array1) // array1: [0 0 0 0 0]

// 声明一个长度为5的整数数组
// 初始化每个元素
array2 := [5]int{12, 123, 1234, 12345, 123456}
array2[1] = 5000
fmt.Printf("array2: %d\n\n", array2[1]) // array2: 5000

// n 是一个长度为 10 的数组
var n [10]int
var i, j int

// 为数组 n 初始化元素
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}

// 输出每个数组元素的值
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j])
}

// 数组 - 5 行 2 列
var a = [5][2]int{{0, 0}, {1, 2}, {2, 4}, {3, 6}, {4, 8}}
var e, f int

// 输出数组元素
for e = 0; e < 5; e++ {
for f = 0; f < 2; f++ {
fmt.Printf("a[%d][%d] = %d\n", e, f, a[e][f])
}
}
}

Pointer 指针

在 Go 语言中,返回一个值的副本可能会消耗大量的内存和处理时间,因此 Go 通常会推荐返回指针。返回指针不仅可以节省内存,而且可以减少对底层数据结构的复制和修改,因为指针是对内存地址的引用,可以直接操作底层数据结构的内存地址。

Goroutine 协程