Go 语言入门指南:基础语法和常用特性解析(1) | 青训营

81 阅读6分钟

什么是Go语言

Go语言是谷歌出品的一门通用型计算机编程语言,Go语言具有以下特点:

  1. 高性能、高并发
  2. 语言简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

基础语法

Hello World!

package main

import (
	"fmt"
)

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

变量

变量类型

变量(Variable)的功能是存储数据。不同的变量保存的数据类型可能会不一样。经过半个多世纪的发展,编程语言已经基本形成了一套固定的类型,常见变量的数据类型有:整型、浮点型、布尔型等。

Go语言中的每一个变量都有自己的类型,并且变量必须经过声明才能开始使用。

变量类型

Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。并且Go语言的变量声明后必须使用。

变量的初始化

Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。

var 变量名 类型 = 表达式

var a = "initial" // 类型推导,不指定类型自动判断

var b, c int = 1, 2 // 一次初始化多个变量

var d = true

var e float64 // 普通声明未赋值

f := float32(e) // 短声明

g := a + "apple"
fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
fmt.Println(g)                // initialapple

常量

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。

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

if else

if 条件表达式 {
	//当条件表达式结果为true时,执行此处代码   
}

if 条件表达式 {
    //当条件表达式结果为true时,执行此处代码  
} else {
    //当条件表达式结果为false时,执行此处代码  
}
package main

import "fmt"

func main() {
	// 条件表达式为false,打印出"7 is odd"
	if 7%2 == 0 {
		fmt.Println("7 is even")
	} else {
		fmt.Println("7 is odd")
	}

	// 条件表达式为ture,打印出"8 is divisible by 4"
	if 8%4 == 0 {
		fmt.Println("8 is divisible by 4")
	}

	// 短声明,效果等效于
	//num := 9
	//if num < 0{
	//	...
	//}
	if num := 9; num < 0 {
		fmt.Println(num, "is negative")
	} else if num < 10 {
		fmt.Println(num, "has 1 digit")
	} else {
		fmt.Println(num, "has multiple digits")
	}
}

for

for init statement; condition expression; post statement {
    // 这里是中间循环体
}

statement是单次表达式,循环开始时会执行一次这里

expression是条件表达式,即循环条件,只要满足循环条件就会执行中间循环体。

statement是末尾循环体,每次执行完一遍中间循环体之后会执行一次末尾循环体

执行末尾循环体后将再次进行条件判断,若条件还成立,则继续重复上述循环,当条件不成立时则跳出当下for循环

package main

import "fmt"

func main() {
	i := 1
	for {
		fmt.Println("loop")
		break // 跳出循环
	}
	
	// 打印7、8
	for j := 7; j < 9; j++ {
		fmt.Println(j)
	}

	for n := 0; n < 5; n++ {
		if n%2 == 0 {
			continue
			// 当n模2为0时不打印,进到下一次的循环
		}
		fmt.Println(n)
	}
	// 直到i>3
	for i <= 3 {
		fmt.Println(i)
		i = i + 1
	}
  // for 循环嵌套
  for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			fmt.Printf("i = %d, j = %d\n", i, j)
		}
	}
}

switch

当分支过多的时候,使用if-else语句会降低代码的可阅读性,这个时候,我们就可以考虑使用switch语句

  • switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
  • switch 语句在默认情况下 case 相当于自带 break 语句,匹配一种情况成功之后就不会执行其它的case,这一点和 c/c++ 不同
  • 如果我们希望再配一条 case 之后,继续执行后面的 case ,可以使用 fallthrough
package main

import (
	"fmt"
	"time"
)

func main() {

	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		// 在此打印"two"并跳出
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
	// 根据现在的时间判断是上午还是下午
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}

数组

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。

package main

import "fmt"

func main() {
	// 声明了长度为5的数组,数组中的每一个元素都是int类型
	var a [5]int
	// 给数组a的第4位元素赋值为100
	a[4] = 100
	fmt.Println("get:", a[2])
	fmt.Println("len:", len(a))

	// 在给数组声明的同时赋值
	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println(b)

	// 声明二位数组
	var twoD [2][3]int
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
}

切片

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

var s []int

类似与声明一个数组,只不过不用填写它的长度

值得一提的是,切片必须先初始化才能使用!

package main

import "fmt"

func main() {

	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])   // c
	fmt.Println("len:", len(s)) // 3

	// 使用append在尾部添加元素
	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println(s) // [a b c d e f]

	c := make([]string, len(s))
	// 将s复制给c
	copy(c, s)
	fmt.Println(c) // [a b c d e f]
	
	fmt.Println(s[2:5]) // [c d e]
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good) // [g o o d]
}

map

在 Go 语言中,map 是散列表的引用,map 的类型是 map[K]V ,其中 K 和 V 是字典的键和值对应的数据类型。map 中所有的键都拥有相同的数据类型,同时所有的值也都拥有相同的数据类型,但是键的类型和值的类型不一定相同。键的类型 K ,必须是可以通过操作符 == 来进行比较的数据类型,所以 map 可以检测某一个键是否存在。

package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknown"]) // 0

	r, ok := m["unknown"]
	fmt.Println(r, ok) // 0 false

	delete(m, "one")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

range

用于遍历,注意以下两点

  1. range在map中遍历顺序是随机的,多次遍历的结果可能不同
  2. range在数组中是从下标0开始递增遍历的,多次遍历的结果是相同的
package main

import "fmt"

func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num) // index: 0 num: 2
		}
	}
	fmt.Println(sum) // 9

	m := map[string]string{"a": "A", "b": "B", "c": "C"}
	for k, v := range m {
		fmt.Println(k, v)
	}
	for k := range m {
		fmt.Println("key", k)
	}
}

函数

函数是指一段可以直接被另一段程序或代码引用的程序或代码,一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能。

package main

import "fmt"

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

// 若类型相同,允许这样写
func add2(a, b int) int {
	return a + b
}

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) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
	v, ok = exists(map[string]string{"a": "A"}, "b")
	fmt.Println(v, ok) //   false
}