一、Go简介

Go是2009年开源的编程语言,Go语言具有以下特性:语法简洁、并发编程、编译迅速、数组安全、丰富的内置类型、错误处理、匿名函数和闭包、反射、函数多返回值、自动垃圾回收。

二、Go语言安装与配置

Go语言支持以下系统:Linux、Mac、Windows。

安装包下载地址:https://golang.org/dl/

如果打不开可以使用这个地址:https://golang.google.cn/dl/

在这里插入图片描述

下面介绍Windows和Linux系统的安装:

1、Windows系统下安装:

可以直接点击.msi进行下载,比如上述图片的go1.19.1.windows-amd64.msi,下载完后打开msi,进行安装,安装目录可以使用默认,也可以自己配置,安装目录不可以有中文。然后配置系统环境变量:添加GOROOT,值为Go的安装目录,例如D:\Program Files\Go。然后可以在cmd里执行go version,如果出现类似输出:go version go1.19.1 windows/amd64,说明安装配置成功,接下来就可以进行开发运行go程序了。

2、Linux系统下的安装:

2)将下载的二进制包解压至/usr/local目录

tar -C /usr/local -xzf go1.19.1.linux-amd64.tar.gz

3)将 /usr/local/go/bin 目录添加至 PATH 环境变量:

export PATH=$PATH:/usr/local/go/bin

以上只能暂时添加 PATH,关闭终端然后下次再登录又没有了。
可以编辑 ~/.bash_profile 或者 /etc/profile,并将以下命令添加该文件的末尾,这样就可以永久生效:

export PATH=$PATH:/usr/local/go/bin

添加后需要执行:

source ~/.bash_profile
或者
source /etc/profile
三、Go语言开发结构与流程

以下程序是在windows系统的VS Code编辑器开发的,安装Go扩展可以方便开发。
Go语言程序的基本结构有以下几个组成:包声明、引入包、函数、变量、语句、表达式、注释。
以下是一个输出Hello, World!的main.go程序:

package main

import "fmt"

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

打开命令行,在保存程序文件的目录中执行go run main.go,输出Hello, World!。

可以使用go build命令来生成二进制文件:

go build main.go
四、Go语言的基础定义

1、标识符:

Go语言标识符与其它语言类似,第一个字符必须是字母、下划线,不能是数字。

2、关键字

Go语言的25个关键字或者保留字:
var, type, switch, struct, select, return, range, package, map, interface, if, import, goto, go, func, for, fallthrough, else, default, defer, const, case, continue, chan, break.
Go语言的36个预定义标识符:
append, bool, byte, cap, close, complex, complex64, complex128, uint16, copy, false, float32, float64, imag, int, int8, int16, uint32, int32, int64, iota, len, make, new, nil, panic, uint64, print, println, real, recover, string, true, uint, uint8, uintptr.

3、注释:

// 单行注释
/*
 xxxxx
 多行注释
 */
五、Go语言的数据类型

有如下类型:
1、数字类型
整型 int 和浮点型 float32、float64,Go 语言支持整型、浮点型数字和支持复数。
2、布尔类型
布尔型的值只可以是常量 true 或者 false。例子:var b bool = true。
3、字符串类型:
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。

六、Go语言变量与常量

1、声明变量与变量赋值

// 声明变量的一般形式是使用 var 关键字:
var variable type
// 也可以一次声明多个变量:
var variable1, variable2 type
// 声明变量时也可以赋值
var temp string = "ssss"
var a, b int = 1, 2

声明了变量并且赋值了,也可以不用写类型。

如果声明了变量没有赋值,那么变量的值为零值。比如var a bool的零值为false。

下面是一些类型数据的零值:

  • 数值类型(包括complex64/128)为0
  • 布尔类型为 false
  • 字符串为 “”
  • 以下几种类型零值为nil:
    var a *int
    var a []int
    var a map[string] int
    var a chan int
    var a func(string) int
    var a error // error 是接口

声明变量更加简洁的方式:

a := 1

相当于:

var a int 
a = 1 

声明全局变量和函数变量:

ackage main

var (  // 这种因式分解关键字的写法一般用于声明全局变量
    a int
    b bool
)

// 这种不带声明格式的只能在函数体中出现
// c, d := 3123, "hello"

func main(){
    c, d := 3123, "hello"
}

2、常量

常量是在程序运行时不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式如下:

const variable [type] = value
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型
    const a string = "abc"
    const a = "abc"
多个相同类型的声明可以简写为:
const c1, c2 = value1, value2

常量可以用len()等内置函数计算表达式的值,在常量表达式中,函数必须是内置函数,否则编译不通过

ackage main

const (
    a = "abc"
    b = len(a)
)

func main(){
    println(a, b)
}

iota是一种特殊常量,可以认为是一个可以被编译器修改的常量。iota在const关键字出现时将被重置为0,const中每新增一行常量声明,iota计数一次。

package main

import "fmt"

func main() {
    const (
        a = iota   //0
        b          //1
        c          //2
        d = "hh"   //独立值,iota += 1
        e          //"ha"   iota += 1
        f = 312    //iota +=1
        g          //312  iota +=1
        h = iota   //7,恢复计数
        i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i) // => 0 1 2 hh hh 312 312 7 8
}

3、变量作用域

作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。

Go语言中变量可以在三个地方声明:

  • 函数内定义的变量(局部变量):作用域只在函数体内,参数和返回值变量也是局部变量。
  • 函数外定义的变量(全局变量):在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。
  • 函数的形式参数:形式参数可以看作函数的局部变量。
七、Go语言运算符

Go语言运算符与java等语言基本一样,均有:算术运算符、关系运算符、逻辑运算符、赋值运算符、位运算符。
比较例外的是,Go语言有与C/C++类似的指针相关运算符:

运算符描述示例
*指针变量*a是一个指针变量
&返回变量存储地址&a: 变量的存储地址
package main

import "fmt"

func main() {
   var a int = 22
   var ptr *int

   // ptr变量存储了a的地址
   ptr = &a
   fmt.Printf("a的值为 %d\n", a);
   fmt.Printf("ptr的值为 %s\n", ptr);
   fmt.Printf("*ptr的值为 %d\n", *ptr);
}

输出:

a的值为 22
ptr的值为 %!s(*int=0xc00000e0a8)
*ptr的值为 22
八、Go语言函数

Go语言程序至少有一个main函数。

1、函数定义

func function_name( [parameter list] ) [return_types] {
   // 函数体
}

2、函数使用

以下代码定义了函数和函数调用:

package main

import "fmt"

func main() {
   var a int
   a = sum(1, 2)
   fmt.Printf("a的值为 %d\n", a);
}

func sum(a, b int) int {
	var c int
	c = a + b
	return c
}

返回多个值的函数:

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("cc", "dd")
   fmt.Println(a, b) // 输出:dd cc
}
九、Go语言条件语句

Go语言条件语句的执行逻辑与其它语言是一致的(但是Go语言多了个select语句),主要是写法不同

1、if语句

定义:

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
}

示例:

package main

import "fmt"

func main() {
   var a int = 1
   if a < 10 {
       // 如果条件为true时执行以下语句
       fmt.Printf("a小于10\n" )
   }
}

if else 语句:

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
} else {
  /* 在布尔表达式为 false 时执行 */
}

2、switch语句

定义:

switch temp {
    case temp1:
        // 
    case temp2:
        //
    default:
        //
}
或者
switch {
    case 布尔表达式1:
        // 
    case 布尔表达式2:
        //
    default:
        //
}

示例:

package main

import "fmt"

func main() {
   var a string = ""
   var num int = 10

   switch num {
      case 123: a = "大于10"
      case 10: a = "等于10"
      case 1, 2: a = "小于10"
      default: a = "不知道"
   }

   switch {
      case a == "等于10":
         fmt.Printf("a等于10!\n" )    
      case a == "大于10", a == "小于10":
         fmt.Printf("a大于或者小于10!\n" )      
      default:
         fmt.Printf("其它情况\n" );
   }

   fmt.Printf("num的值为: %d, a的值为: %s", num, a );
}

输出:

a等于10!
num的值为: 10, a的值为: 等于10

fallthrough

使用fallthrough会强制执行后面的case语句,使用fallthrough时不会判断下一条 case 的表达式结果是否为 true,直接执行。

package main

import "fmt"

func main() {

    switch {
    case false:
            fmt.Println("1、case条件为false")
            fallthrough
    case true:
            fmt.Println("2、case条件为true")
            fallthrough
    case false:
            fmt.Println("3、case条件为false")
            fallthrough
    default:
            fmt.Println("默认case")
    }
}

输出:

2、case条件为true
3、case条件为false
默认case

3、select语句

select语句随机执行一个可运行的case,如果没有可运行的case,将阻塞运行,直到有 case 可运行,
每个case都必须是一个通信

定义:

select {
    case a: // 通信a
       // 
    case b: // 通信b
       //
    default:
       //
}
十、Go语言循环语句

Go语言循环的语法有点差别,for循环的写法也有差异,没有while循环。
示例:

// 循环遍历
for i := 0; i < count; i++ {
    //
}

// 类似map遍历
for _, v := range map {
    // 
}

// 
for 布尔表达式 {
    // 
}
示例:
for 1 > 0 {
    fmt.Printf("111")
}

go循环程序中可以用break、continue、goto语句控制循环执行过程。

十一、Go语言数组

Go语言数组是具有相同唯一类型的一组长度固定的数据结构,这种类型可以是任意的原始类型例如整型、字符串,也可以是自定义类型。可以通过索引来读取或者修改数组元素。

1、一维数组

1)、数组声明:

var arr_name [size] type

例如:

var arr [3] int

2)、初始化数组

var arr = [2]float32{2.1, 3.3}

或者

arr := [2]float32{2.1, 3.3}

如果数组长度不确定,可以使用…代替数组的长度,编译器会根据元素个数自行推断数组的长度:

var arr = [...]float32{2.1, 3.3}
或者
arr := [...]float32{2.1, 3.3}

如果设置了数组的长度,还可以通过指定数组下标来初始化元素或者修改元素值:

arr := [2]float32{2.1, 3.3}
arr[1] = 1.1

2、多维数组

1)数组声明

var arr [size1][size2]...[size_n] type

例如三维数组:

var arr [4][22][3] int

下面以二维数组举例:

二维数组本质上是由一维数组组成的。

arr := [][]int{}

// 可以使用append()函数向空的二维数组添加两行一维数组
row1 := []int{1, 2, 3}
row2 := []int{4, 5, 6}
arr = append(arr, row1)
arr = append(arr, row2)

fmt.Println("arr为:", arr)
fmt.Println("第一个元素为:%d", arr[0][0])

也可以这样初始化二维数组:

arr := [2][4]int{
 {0, 1, 2, 3} ,
 {4, 5, 6, 7} , // ,不能省略
}

二维数组取值赋值:

赋值:
arr[2][1] = 222
取值:
a := arr[2][1]

3、数组作为函数参数:

void fun(param [3]int)
{
    //
}
或者不指定数组长度
void fun(param []int)
{
    //
}
十二、Go语言结构体

结构体是由相同类型或不同类型的数据组成的数据集合。

1、定义与使用

type struct_name struct {
   filed1 type
   filed2 type
   filed3 type
}

示例:

package main

import "fmt"

type Book struct {
   name string
   author string
}

func main() {
    stru := Book{"编程设计", "xian"}
    fmt.Println("stru为:", stru)

    var book1 Book
    book1.name = "js程序设计"
    book1.author = "xxx"
    fmt.Println("book1: ", book1)

    // 也可以使用key => value格式
    fmt.Println(Book{name: "编程设计", author: "xian"})
}

输出:

stru为: {编程设计 xian}
book1:  {js程序设计 xxx}
{编程设计 xian}

访问结构体成员:

结构体.成员名

2、结构体作为参数传递:

package main

import "fmt"

type Book struct {
   name string
   author string
}

func main() {
    stru := Book{"编程设计", "xian"}
    fmt.Println("stru为:", stru)

    getInfo(stru)
}

func getInfo( book Book ) {
    fmt.Println( "Book name为: ", book.name)
}

输出:

stru为: {编程设计 xian}
Book name为:  编程设计

3、结构体指针

可以定义指向结构体的指针:

var struct_pointer *结构体名称 // 注意不是结构体变量名称

获取结构体变量地址:

struct_pointer = &str

使用指针访问结构体成员:

struct_pointer.name
十三、Go语言集合(Map)

Map是一种无序的键值对的集合。

1、定义map

// 声明变量,默认map是nil
var map_variable map[key_type]value_type

// 或者使用make函数
map_variable := make(map[key_type]value_type)

示例:

package main

import "fmt"

func main() {
    var capitalMap map[string]string
    capitalMap = make(map[string]string)

    capitalMap["France"] = "巴黎"
    capitalMap["English"] = "伦敦"
    // capitalMap := map[string]string{"France": "巴黎", "English": "伦敦"}

    // 使用range遍历
    for k := range capitalMap {
        fmt.Println(k, " => ", capitalMap[k])
    }

    // 或者
    fmt.Println("\nk, v遍历: ")
    for k, v := range capitalMap {
        fmt.Println(k, " => ", v)
    }

    fmt.Println("\n")
    // 查看元素在集合中是否存在 */
    capital, ok := capitalMap["Itlay"]
    if (ok) {
        fmt.Println("Itlay的首都 ", capital)
    } else {
        fmt.Println("不存在Itlay的首都")
    }
}

delete删除:

delete(capitalMap, "France")
十四、Go语言range

for range循环可以对数组、字符串、slice、map等进行循环遍历。

1、遍历map

// 遍历map
for key, value := range map {
    map[key] = value
}

2、遍历数组

// 遍历数组
for i, val := range arr {
    // i代表索引,val代表相应的值
}

3、遍历字符串

// 遍历字符串。i是字符的索引,char是字符(Unicode的值)本身。
for i, char := range str {
    fmt.Println(i, char)
}
十五、Go语言切片

Go语言数组长度是固定的, 而切片提供了一种动态长度数组的功能。

1、创建切片与使用

可以使用make创建切片,len是数组的长度也是切片的初始长度。

var slice1 []type = make([]type, len)
或者
slice1 := make([]type, len)
// 也可以指定容量,capacity是可选参数
slice1 := make([]type, len, capacity)

切片初始化:

slice1 := []int{1,2,3 } 

截取:

s := arr[startIndex:endIndex] // 左闭右开
s := arr[:endIndex]
s := arr[startIndex:]

len()方法获取切片长度,cap()测量切片容量。

切片在未初始化之前默认为nil,长度为 0:

var slice1 []int
// slice1 == nil

2、append()函数:切片追加新元素:

package main

import "fmt"

func main() {
    var slice1 []int // 创建切片
    fmt.Printf("长度=%d, 容量=%d, 切片=%v\n", len(slice1), cap(slice1), slice1)

    // 向切片添加一个元素
    slice1 = append(slice1, 33)
    fmt.Printf("长度=%d, 容量=%d, 切片=%v\n", len(slice1), cap(slice1), slice1)
}

输出:

长度=0, 容量=0, 切片=[]
长度=1, 容量=1, 切片=[33]

3、copy: 拷贝切片

package main

import "fmt"

func main() {
    var slice1 []int // 创建切片
    slice2 := []int{32, 43} // 创建切片

    // 向切片添加一个元素
    slice1 = append(slice1, 33)

    // slice1拷贝到slice2
    copy(slice2, slice1)

    fmt.Printf("长度=%d, 容量=%d, 切片=%v\n", len(slice2), cap(slice2), slice2) // => 长度=2, 容量=2, 切片=[33 43]
}
十六、Go语言接口
// 定义接口
type interface_name interface {
    method_name1 [return_type]
    method_name2 [return_type]
    method_name3 [return_type]
    ...
    method_name_n [return_type]
}
 
// 定义结构体
type struct_name struct {
    variable1 type
    variable2 type
    ...
    variable_n type
}
 
// 实现接口方法
func (struct_variable_name struct_name) method_name1() [return_type] {
    /* 方法实现 */
}

示例:

package main

import (
    "fmt"
)

type Phone interface {
    speak()
}

type IPhone struct {
    name string
}

func (iPhone1 IPhone) speak() {
    fmt.Println("iPhone1 speak")
}

func main() {
    var phone Phone

    phone = new(IPhone)
    phone.speak()
}
十七、Go语言并发

Go语言并发:通过go关键字来开启goroutine。goroutine是轻量级线程,goroutine的调度由Golang运行时进行管理。go语句开启一个新的运行期线程,即就是goroutine,以一个不同的、新创建的goroutine来执行一个函数。同一个程序中的所有goroutine共享同一个地址空间。

1、goroutine 语法格式:

go 函数名(参数列表)

示例:

package main

import (
    "fmt"
    "time"
)

func printFun(s string) {
    for i := 0; i < 3; i++ {
        time.Sleep(200 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go printFun("World")
    printFun("Hello")
}

2、使用通道(channel)传递数据

可以使用通道(channel)用来传递数据。通道可在两个goroutine之间通过传递一个指定类型的值来通讯。操作符<-指定通道的方向:发送或接收。如果没有指定方向,说明是双向通道。

创建通道:

chan1 := make(chan int)
chan1 <- val    // 把 val 发送到通道 chan1
temp := <-chan1  // 从 chan1 接收数据, 并且赋值给temp变量

示例:

package main

import "fmt"

func sum(nums []int, c chan int) {
    sum := 0
    for _, v := range nums {
            sum += v
    }
    // 把sum发送到通道c
    c <- sum 
}

func main() {
    a := []int{31 , 21, 3, 4}

    chan1 := make(chan int)
    go sum(a[:len(a)/2], chan1)
    go sum(a[len(a)/2:], chan1)
    sum1, sum2 := <-chan1, <-chan1 // 从通道c中接收两个sum结果

    fmt.Println(sum1, sum2, sum1 + sum2) // => 7 52 59
}