学习Go语言基础的总结|青训营笔记

55 阅读8分钟

语言特性

1、天然支持高并发

Go语言内置了goroutine和channel,提倡通过信道来共享内存,而不是通过共享内存来实现通信。高并发的高得益于goroutine的轻量以及其在语言层面实现了调度。goroutine的开销非常小,在KB级别且创建和销毁非常快。GO语言的线程调度不依靠操作系统内核复杂的线程调度,这也很大程度减小了系统开销。

2、丰富的标准库

Go语言拥有丰富的标准库,如fmt(处理输入输出,格式化文本等功能),os(提供了与操作系统交互的功能,如操作文件、环境变量等),http/net(用于网络编程,向服务器发送请求,以及创建服务器等),io(处理读取和写入操作),regexp(提供正则表达式功能,匹配字符串是否符合规则)等,这仅仅只是本人学习了解过的一些标准库,更多标准库有待探索。

3、支持跨平台

Go语言编译会直接将代码编译成二进制代码,通过静态链接将该文件所依赖的所有库一同打包。通过交叉编译直接编译成目标系统能够识别的二进制代码,从而实现跨平台。

4、语法简单

Go语言语法较其他语言如C、C++、C#、Java等的流程控制语法,Go对其进行了简化或者优化,循环也只有一种for循环,switch中case后可接多个任何类型的表达式,变量的赋值和创建也支持自动推测等。

基础语法

1、变量声明

Go语言声明变量通过 var 关键字,也支持 := 来自动推测右部类型( := 只能在函数内部使用)

var a int              //声明一个int类型的变量
var(
    b type
    c type
    d type
)                      //一次声明多个不同类型的变量
b := "hello world"     //根据右部类型声明一个变量并将右部的值赋给它

2、结构体和接口定义

Go语言中没有类,但是其通过结构体和接口实现的面向对象的特性,结构体和接口的定义通过 type 关键字实现。接口是一种抽象类型,可以在其里面声明方法。结构体实现接口中声明的所有方法便可视为该结构体实现了该接口,关于怎么为结构体实现方法在接下来的函数声明中说明。

//定义一个结构体
type Student struct{
    Name string        //为Student结构体声明一个Name字段
}
    
//定义一个接口
type Product interface{
    getId()            //为Product接口声明一个getId方法
}

注意:Go中字段名的大小写会决定该字段的可访问性,同学们可以去测试在不同包中访问结构体大小写字段,如果想要该字段小写可以通过tag实现,格式为两个反引号标注的字符串如:json:"id"

3、函数声明

函数声明通过func关键字实现,支持匿名函数以及闭包。

//一般方法声明格式
//func 方法名(参数列表)返回值{
//    方法体
//}
//声明一个一般方法
func add(a,b int)int{
    return a+b
}
//为结构体实现方法格式,接收者出可以用一个变量接收,也可以是一个指针
//func (接收者)方法名(参数列表)返回值{
//      方法体
//}
type Person struct{
    Id int
}

func (p Person)getId()int{
    return p.Id
}

//如果这时有一个接口的函数列表中的所有函数是该结构体所有函数的子集,那么我们便可以
//看做该结构体实现了这个接口,这个接口便可接收这个结构体的实例,类似于经典面向对象中的多态
type ID interface{
    getId() int
}

func main(){
    var i ID
    i = Person{Id:101}
    fmt.Printf("%v\n",i.getId())    //运行控制台输出“101”
}

匿名函数可以只用一次,也可以用一个变量保存。函数的闭包中声明的变量会一直存在直到外部函数生命周期结束,内部函数可以直接访问到外部函数中的变量。

//函数的闭包,此处为匿名函数
func fun() func() int {
   count := 0
   return func() int {
      count++
      return count
   }
}
//在main函数中测试
func main() {
   f := fun()
   fmt.Println(f()) // 输出:1
   fmt.Println(f()) // 输出:2
}

4、流程控制

Go中的流程控制也有选择,循环,跳过,关键词和c++、java中的一样,如if - else if - else,switch,for,goto等。 在使用if - else if -else时,Go省略了判断处不必要的括号,同时还支持在第一个布尔表达式之前执行一次操作并用";"隔开,产生的新变量会是一个局部变量,只能在这个分支结构内使用。

//if-else if-else使用,else if 可用有0-n个
if [表达式] ; 布尔表达式1 {
}else if 布尔表达式2 {
}else{
}

由于在分支过多时大量的else if会使代码可读性大大降低,Go更推荐用其优化过的switch。switch需配合case一起使用,一个分支一个case,同时执行完一个分支后默认也不再是fall-through,所有不需程序员手动写break,而是在需要fall-through时加上fallthrough关键字。并且,case后面也可以是各种类型的表达式。

a := 3
switch a{
    case 1:
        fmt.Println("走了第一个分支")
    case 2,3,4:                          //case后面可接任意多个表达式
        fmt.Println("走了第二个分支")
    case "aaa":
        fmt.Println("走了第三个分支")
        fallthrough                       //欲fallthough则需手动调用而不是默认为fallthrough
    case float:
        fmt.Println("走了第四个分支")
    default:
        fmt.Println("走了默认分支")
}

Go中的循环只有一个关键字 for 我们可以通过改变 for 关键字后面的表达式的个数来改变 for 循环的表现。同时也可以配合break和continue使用如:

for{                                 //表示死循环,注意使用break退出
}

for a := rand.Int()%100;a<50{        //为整个循环定义一个局部变量
}

for a := rand.Int()%100;a>0;a--{     //随机执行循环体0-100次
}

5、数组

Go中的数组声明格式:

//var 变量名 [数组长度]数组类型
var arr1 [5]int
//变量名 := [数字长度]数组类型{初始化列表}
arr2 := [5]int{1,2,3,4,5}

遍历时也可以通过range关键字实现,返回两个值,一个索引,一个该值。

for i,n := range arr2{
    fmt.Printf("索引%v处为%v\n",i,n)
}
//输出:
//索引0处为1
//索引1处为2
//索引2处为3
//索引3处为4
//索引4处为5

6、切片

切片可以看做是一种比较特殊的数组,它的长度会随着元素的数量自动扩充,可以通过len和cap获得切片的长度和容量。长度是指该切片的元素个数,容量是指该切片的从该切片第一个元素起到其父切片的最后一个元素的个数(无父元素则长度等于容量),什么是父切片会在接下来的切片语法中讲到。

//切片是这样定义的
var s1 []int       //这样的切片不可直接使用,它只能接收同类型的切片,当切片长度不为0时才能使用。
s2 := make([]类型名,长度)//元素数为长度,所有元素在被创建时赋予0值。
s3 := []int{初始化列表}  //元素数为长度,所有元素按初始化列表中的顺序依次赋值。

切片也可以像数组一样使用range进行遍历,返回值同样是索引和值,这里不再赘述。 切片可以进行切分,我们通过用左右两个所有进行切分便可将中间的元素作为一个切片返回(左闭右开)。如:

a := []int{1,2,3,4,5,6,7,8,9}
//切片语法(对应的值都对应着同一个地址)
a[:]      //表示长度和容量均与原切片相同
a[l:]     //表示长度和容量为原切片长度减l的切片
a[:r]     //表示长度为原切片长度-r,容量为与原切片容量相同的切片
a[l:r]    //表示长度为r-l,容量为原切片容量-l的切片

注意:不管切片被切了多少次,它们最终都是公用原始数组的那一块地址。

7、映射

在Go中讲到的映射就是map,存储了一系列键值对,将键值与实际的值绑定。

//map可以这样声明
var m1 map[string]类型  
//这样定义可用访问空的键返回0值,但是不能增加键,会造成程序恐慌但是可以接收一个map如:
var m2 = map[string]类型{实例化列表}

m3 := make(map[string]类型) //后两种实例化的map的方法便可以正常使用,也可以用一个同类型的map覆盖
//map增加和取值操作如下:
//v1 := m3[键值]
//m3[键值] = 对应类型的具体值
m4 := make(map[string]int)
m4["first"] = 1         //将1与键值 "first" 绑定
v := m4[first]          //取出键值 "first" 对应的值

至此,基础的语法已经讲解完毕。欲学习更多详细基础语法请点击这里,欲学习标准库以及框架请锁定Go官网

总结

本人对golang学习也有半月之久,由于有其他语言的基础,所以基础语法部分学的比较快,在学习过程中我会将golang的一些语法与其他语言比较,尝到了golang带给我的甜头。既然已经入门了基础语法,下面我将会深入钻研golang的标准库,并打算让golang陪我走更长远的路。