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")
}
输出结果为
number为30
number为40
补充: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语言中的结构体、集合、接口、协程等部分知识有了一定的学习和使用。在今后的使用过程中,需要注意的是,接口中的方法不能有方法体,接口中不含变量;对于通道而言,要注意通道容量大小,当通道为空或满时操作通道会报错。