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

117 阅读10分钟

Go语言入门基础语法

本文主要记录了Go语言基础语法中的常用语句及使用示例。

1. 数据类型

布尔型、整型、浮点型、字符串类型、派生类型。

2. 变量与常量

2.1 变量

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字

第一种 关键字var声明变量
var var_name type
var name string = "Li Hua" //声明变量并初始化
第二种 直接赋值 自行判断类型
var name = "Li Hua" 
第三种  := 声明
name := "Li Hua"

2.2 常量

常量由关键字const定义: const con_name type

const weight = 10

3. 条件语句

3.1 if 语句

if 布尔表达式 {
    //执行语句
}

3.2 if...else 语句

if 布尔表达式 {
  //执行语句
} else {
  //执行语句
}

3.3 switch 语句

go语言中的switch中从下逐一测试case,匹配为止,不需要添加break,默认自带break。若想执行匹配之后的case语句,可以添加fallthrough。

var number int = 30
switch number {
	case 10:
		fmt.Println("number为10")
	case 20:
		fmt.Println("number为20")
	case 30:
		fmt.Println("number为30")
		fallthrough   //匹配到30后 继续执行后面的语句
	case 40:
		fmt.Println("number为40")
	}

输出结果为

number30
number40

补充:switch中的default,无论放在哪里,都是最后执行。

3.4 select 语句

select语句类似于switch语句,但select语句只能用于通道操作,每个case必须是一个通道。
(1)select语句会监听每一个通道上的操作,一旦其中有一个通道准备好就会执行相应的代码。
(2)若多个通道准备就绪,随机挑选一个执行。
(3)如过没有通道可执行,有default代码时,执行default代码块,否则,select将阻塞。

4. 循环语句

4.1 三种for循环

Go语言中的for循环结构有三种形式。
for init; condition; post { }

init:赋值表达式,给控制变量赋初值;
condition:关系表达式或逻辑表达式,循环控制条件;
post: 赋值表达式,给控制变量增量或减量。

(1)第一种
和c语言的for一样

循环结构 求1-10的和:
var sum int
for i := 1; i <= 10; i++ {
    sum += i
}
fmt.Printf("求和为:%d", sum)

输出结果为:

求和为:55

(2)第二种 省略了init和post

var sum int = 1
for sum <= 10 {
    sum += sum
    fmt.Printf("此时的sum值为:%d\n", sum)
}
fmt.Printf("求和为:%d", sum)

输出结果为:

此时的sum值为:2
此时的sum值为:4
此时的sum值为:8
此时的sum值为:16
求和为:16

(3)第三种 将init,condition,post都省略,成为了一个无限循环。

for {
    //执行的代码
}

4.2 break语句

Go 语言中,break 语句用于终止当前循环或者 switch 语句的执行,并跳出该循环或者 switch 语句的代码块。

跳出for循环
var sum int
for j := 1; j <= 10; j++ {
    if j%5 == 0 {
	fmt.Printf("跳出循环时j=%d\n", j)
	break
    }
    sum += j
}
fmt.Printf("求和为:%d", sum)

输出结果为:

跳出循环时j=5
求和为:10

补:break语句可跳出指定的的for循环,只需在for前面加上re:即可

continue语句使用与C语言用法一样。

5. 函数

Go语言中函数的定义格式:

func  函数名(参数列表) 返回类型 {
  //执行代码
}

示例:定义add函数为求和函数

//主函数
func main() {
    a := 3
    b := 4
    sum := add(a, b)
    fmt.Printf("%d + %d = %d", a, b, sum)
}
//add求和函数
func add(a, b int) int {
    return a + b
}

输出结果:

3 + 4 = 7

补充:Go语言中函数可以同时返回多个值,接收时也用多个变量接收值。

6. 数组

Go语言中的数组使用大致与C语言中的数组一样,只是在声明时有点不一样。
声明数组:var 数组名 [size]类型

var array1 [5]int  //声明一个大小为5的数组为array1
var array2 = [5]int{1, 2, 3, 4, 5}  // 声明指定大小数组 并初始化
array3 := [3]int{11, 12, 13}      //声明数组 := 形式
var array4 = [...]int{1, 2, 3, 4}  //声明一个长度不确定的数组 ... 代替长度
var array5 = [4]int{1: 99, 3: 98}  //声明数组的同时 指定数组的具体索引位置的元素
var n = array5[1]  //取值

7. 指针

Go语言中的指针使用与C语言中的指针类似,声明格式:var 指针名 *类型
指针使用示例:

var number int = 100
var ptr *int
ptr = &number
fmt.Printf("number数值为:%d\n", number)
fmt.Printf("ptr指针指向数值为:%d\n", *ptr)
fmt.Println(ptr == &number)

输出结果为:

number数值为:100
ptr指针指向数值为:100
true

总结

本文主要记录了Go语言中基础语法的个别使用示例,主要为了能够后期在回顾复习时,可以快速地回忆和使用,基础语法中有几个知识点容易和其他语言的用法混淆,例如:对于变量的声明,Go语言中变量名在前,变量类型再后;而C语言中却恰恰相反。同样函数名与函数的返回值类型、数组名与数组的类型都是如此,这是我们初学者需要注意区分的点。对于循环结构for循环,Go语言中提供了三种方式,但第三种方式通常会形成一个无限循环,相比较而言,使用情况较少。

==========跟新学习笔记==========

如下是Go语言基础语法学习笔记第二部分

8. 结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义结构体

type 结构体名 struct {
    成员  类型
    成员  类型
    ......
}

使用示例

定义了一个Student结构体,内部成员有姓名和年龄,可以将成员看作是该结构的属性理解。

//定义结构体
type Student struct {
	name string
	age  int
}
// 1. var关键字 声明并初始化结构体 
var student1 Student = Student{"Li Hua", 18}

// 2. := 声明并初始化结构体
student2 := Student{"zhang san", 10}
//key-value形式初始化结构体
student3 := Student{name: "Li Si", age: 11}

var student4 Student
// 3 通过"."操作符给结构体内赋值
student4.name = "Wang wu"
student4.age = 20

// 4 结构体指针
var stu_pointer *Student
stu_pointer = &student1

//输出
fmt.Println(student1)
fmt.Println(student2.name)
fmt.Println(student3)
fmt.Println(student4)
fmt.Println(stu_pointer.name)

输出结果:

{Li Hua 18}
zhang san
{Li Si 11}
{Wang wu 20}
Li Hua

9. 切片Slice

切片是对数组的升级,由于Go语言数组长度不可变,在特定场景中这样的集合不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
声明切片 : var slice_name []type

  • 切片不需要说明长度
// 1. 声明一个切片 并初始化 1, 2, 3
var slice1 []int = []int{1, 2, 3}
fmt.Printf("slice= %v\n", slice1)

// 2. 使用make()函数来创建切片 make([]type, len, cap) 参数分别代表切片类型、切片长度、切片容量
var slice2 []int = make([]int, 5, 10)
fmt.Printf("slice2= %v, 长度= %d , 容量= %d \n", slice2, len(slice2), cap(slice2))

// 3. 通过append()函数向切片内添加元素, 可同时添加多个元素
slice1 = append(slice1, 5, 6, 7)
fmt.Printf("slice1= %v, 长度= %d , 容量= %d \n", slice1, len(slice1), cap(slice1))

// 4. 通过下标索引获取切片内的数据元素 slice[startIndex:endIndex] 获取为左闭右开
fmt.Printf("slice1[:3]= %v \n", slice1[:3])
fmt.Printf("slice1[4:5]= %v", slice1[4:5])

输出结果:

slice= [1 2 3]
slice2= [0 0 0 0 0], 长度= 5 , 容量= 10 
slice1= [1 2 3 5 6 7], 长度= 6 , 容量= 6
slice1[:3]= [1 2 3]
slice1[4:5]= [6]

append()函数测试

使用append()函数中追加元素时,切片的长度和容量大小的变化测试。

var slice1 []int = make([]int, 3, 3)
fmt.Printf("slice1= %v, 长度=%d, 容量=%d \n", slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 5, 6, 7)
fmt.Printf("slice1= %v, 长度=%d, 容量=%d \n", slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 8)
fmt.Printf("slice1= %v, 长度=%d, 容量=%d \n", slice1, len(slice1), cap(slice1))

输出结果:

slice1= [0 0 0], 长度=3, 容量=3 
slice1= [0 0 0 5 6 7], 长度=6, 容量=6 
slice1= [0 0 0 5 6 7 8], 长度=7, 容量=12

小结:

  • 对切片中的长度len和容量cap的理解,长度可理解为切片内当前已存储的数据元素个数,如果为初始化,默认每个数据元素值为0;而容量为切片总共能够存储的元素个数。
  • 使用append()函数时,当切片len<cap时,直接添加元素;当len=cap时,回按照原来的大小x2倍放大,然后进行追加元素。

10. Map集合

Map 是一种无序的键值对的集合,可以通过 key 来快速检索数据,key 类似于索引,指向数据的值。Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

定义Map

使用函数make或关键字ma来定义集合 : map_name := make(map[key类型]value类型, 初始容量) 其中初始容量为可选参数,如果不指定初始容量,Go语言回根据实际情况选择一个合适的值。

使用示例:

创建map集合

// 1. 创建了一个空的map
map0 := make(map[string]string)

// 2. 使用make创建map集合
map1 := make(map[int]string, 3)
map1[1] = "张三"
map1[2] = "Li Si"
map1[3] = "王五"

// 3. 使用map关键字创建map集合
map2 := map[string]string{
    "name": "张三",
    "sex":  "male",
}

操作集合元素

// 从结合map1中获取key为1的元素值
value := map1[1]
// 删除集合map1中key为2的元素值
delete(map1, 1)
// 修改元素
map1[3]="Li Hua"
// 获取集合长度
length := len(map1)
//遍历集合 使用range(下一节讲到)
for key, value := range map1 {
    fmt.Println(key, value)
}

11. 范围Range

Go 语言中range关键字用于fo 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返会 key-value 对。

数组示例


var array [5]int = [5]int{1, 2, 3, 4, 5}

// 1. 只有i时,返回的时数组的下表索引
for i := range array {
    fmt.Println(i)
}

// 2. i,v 时返回的是数组的下表和对应的值
for i, v := range array {
    fmt.Println(i, v)
}

// 3. 可用 _, 丢弃接收值
for _, v := range array {
    fmt.Println(v)
}

输出结果:

0
1
2
3
4
0 1
1 2
2 3
3 4
4 5
1
2
3
4
5

集合示例

map1 := make(map[int]string, 3)
map1[1] = "张三"
map1[2] = "Li Si"
map1[3] = "王五"
fmt.Println(map1)

for key, value := range map1 {
    fmt.Println(key, value)
}

输出结果:

2 Li Si
3 王五
1 张三

12. 递归函数

Go 语言支持递归,当我们使用递归时,需要设置退出条件,否则递归将陷入无限循环中,使用过程与其他语言中类似,如下通过求阶乘展示递归函数使用。
示例:通过递归函数实现求阶乘。

func main() {
    n := 15
    fmt.Println(method(n))
}
func method(n int) int {
    if n <= 1 {   //函数出口
        return n  
    }
    return n * method(n-1)
}

13. 类型转换

类型转换:将一种数据类型的变量转换为另外一种类型的变量。 基本格式:转换类型 (待转表达式)

var n int = 10
m := strconv.Itoa(n)
fmt.Println(m)
var message string = "123"
num, error := strconv.Atoi(message)
if error != nil {
    fmt.Println("转换错误", error)
} else {
    fmt.Println("转换成功", num)
}

14. 接口类型

Go 语言中的接口数据类型,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
基本格式 :

func (变量名 自定义数据类型) 接口中的方法(参数列表) 返回类型列表 {
    //执行代码
}

通过如下的示例来演示接口的简单使用,定义一个Animal接口,内部有vocal()方法,用Cat和Dog结构体分别去实现接口。

// 定义Animal接口
type Animal interface {
	vocal() string  //声明没有实现的方法
}

// 定义Cat结构体
type Cat struct{}
// 用Cat实现Animal接口
func (c Cat) vocal() string {
	fmt.Printf("这是一只猫, 喵喵喵\n")
	return "猫"
}

// 定义Dog结构体
type Dog struct{}
// 用Dog实现Animal接口
func (d Dog) vocal() string {
	fmt.Printf("这是一只狗, 汪汪汪\n")
	return "狗"
}

func main() {
    var animal1 Animal = Cat{}  //实现了多态设计 可以理解为子类的对象指向父类的引用
    var animal2 Animal = Dog{}
    fmt.Println(animal1.vocal())
    fmt.Println(animal2.vocal())
}

输出结果:

这是一只猫, 喵喵喵
猫
这是一只狗, 汪汪汪
狗

需要注意的是,接口类型变量可以存储任何实现了该接口的类型的值。

  • 实现了接口:指实现了接口内的所有抽象方法。
  • 接口中的方法不能有方法体。
  • Go中的实现是隐士实现的,不像Java中的实现必须指明具体的接口类;Go中的实现是基于方法的实现。
  • 接口中不含任何变量。

15. 协程与管道

协程Goroutine

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 语法格式:go 函数名(参数列表)
主线程和两个协程同时执行

func main() { 
	go sayTwo("李四")  // 协程开始
	go sayOne("张三")  // 协程开始
	for i := 1; i <= 10; i++ {  // 主线程开始
		fmt.Println("main", strconv.Itoa(i))
		time.Sleep(time.Microsecond)
	}
}

func sayOne(str string) {
	for i := 1; i <= 10; i++ {
		fmt.Println("sayOne:", str, strconv.Itoa(i))
		time.Sleep(time.Microsecond)
	}
}
func sayTwo(str string) {
	for i := 1; i <= 10; i++ {
		fmt.Println("sayTwo:", str, strconv.Itoa(i))
		time.Sleep(time.Microsecond)
	}
}

协程的特点:

  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级线程

注意:

  • 如果主线程退出了,则协程即使没有执行完毕,也会退出。
  • 当主线程没有结束,协程可提前结束。

管道Channel

定义/声明管道

var 管道名 chan 数据类型

var chan1 chan int  //声明管道chan1 并只能存储int类型数据
var chan2 chan map[int]string //声明管道chan1 并只能存储map类型数据
var chan1 chan int   // 声明管道
chan1 = make(chan int, 3)   // make初始化管道
for i := 1; i <= 3; i++ {
    chan1 <- i   //向管道存数据
}
var n1 int = <-chan1  //从管道取元素
var n2 int = <-chan1
fmt.Println(n1, n2)

注意:

  • 管道是引用数据类型
  • 管道必须初始化后才能写入数据,即make后才能使用
  • 管道只能输入对应类型的数据

总结

本文主要记录了Go语言中基础语法的使用,对Go语言中的结构体、集合、接口、协程等部分知识有了一定的学习和使用。在今后的使用过程中,需要注意的是,接口中的方法不能有方法体,接口中不含变量;对于通道而言,要注意通道容量大小,当通道为空或满时操作通道会报错。