GO 语言学习专篇

562 阅读11分钟

1. GO 环境安装

Windows系统

安装包下载地址为:golang.org/dl/ 或者 golang.google.cn/dl/

安装.msi文件


2. GO基础知识学习

语言数据类型

  • 布尔型: bool

    • 布尔型的值只可以是常量 true 或者 false
    • 在格式化输出时,可以使用 %t 来表示你要输出的值为布尔型。
    • 对于布尔值, 好的命名能够很好地提升代码的可读性,例如以 is 开头的isFinished。

    var b bool = true

  • 数字类型:

    • 数字类型的默认值是 0
    • int 型是计算最快的一种类型
    • Go是强类型语言,不会进行隐式转换。所以Go 中不允许不同类型之间的混合使用,但允许常量之间的混合使用。
    • 尽可能地使用 float64,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。
    • 有符号整数
      • int8(-128 -> 127)
      • int16(-32768 -> 32767)
      • int32(-2,147,483,648 -> 2,147,483,647)
      • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
      • int( 32 位操作系统上64 位,64 位操作系统64 位)
    • 无符号整数
      • uint8(0 -> 255)
      • uint16(0 -> 65,535)
      • uint32(0 -> 4,294,967,295)
      • uint64(0 -> 18,446,744,073,709,551,615)
      • uint ( 32 位操作系统上64 位,64 位操作系统64 位)
    • 浮点数
      • float32(+- 1e-45 -> +- 3.4 * 1e38)
      • float64(+- 5 1e-324 -> 107 1e308)
    • 复数
      • complex64 (32 位实数和虚数)
      • complex128 (64 位实数和虚数)
    var a uint8 = 255 // 255
    var b int = 255 // 255
    var c int // 0
    var d float64 //0
    
  • 字符串类型: string

  • byte类型

  • rune类型

变量

  • 变量通过变量名访问
  • 变量名由字母、数字、下划线组成,其中数字不可以在守卫
  • 声明变量使用 var, 可以一次性声明多个变量
  • 声明变量的格式: var identifier type, 可以只声明,不初始化.
  • 声明未初始化的时候不可以漏写类型, 否则会报错
  • 单引号是字符, 会输出对应的 unicode编码, 双引号是字符串

省略 var, 格式为i := 10, 需要注意的是 :=的左侧必须是新的变量, 不能是已经声明过的,否则会产生编译错误,格式:

有效声明变量名如下

image.png

变量未初始化的初始值:

// 基本数据类型
package main
import "fmt"

func main() {
  var i int
  var f float64
  var b bool
  var s string
  fmt.Printf("%v %v %v %q\n", i, f, b, s) // 0 0 false ""
}

image.png

// 其他数据
package main

import "fmt"

func main() {
  var a *int // 报错: cannot use a (type *int) as type string in argument to fmt.Printf. Go build failed.
  var b []int // []
  var c map[string] int  // map[]
  var d chan int  // <nil>
  var e func(string) int // <nil>
  var f error //  <nil>       error 是接口
  fmt.Printf(a, b, c, d, e, f)
}

常量

常量是不会被修改值的标识符, 常量的数据类型只可以是布尔型、数字型和字符串型。

常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过

iota,特殊常量,可以认为是一个可以被编译器修改的常量, 它默认开始值是0,const中每增加一行加1,同行值相同, iota中断必须显性恢复,

// 定义如下:
const b string = "abc"
const b = "abc" // 编译器可以推断类型
const a, b, c = 1, false, "str" // 多值多类型定义

// 常量枚举
const (
    Unknown = 0
    Female = 1
    Male = 2
)

// 函数计算式
a = "abc"
b = len(a)
// import "unsafe"
c = unsafe.Sizeof(a)

// iota
const (
    a = iota
    b
    c
) // a = 0, b = 1, c= 2

// iota会存储常量的值
const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //独立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢复计数
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)  // 0 1 2 ha ha 100 100 7 8

// iota实例
const (
    i=1<<iota
    j=3<<iota
    k
    l
)

func main() {
    fmt.Println("i=",i)
    fmt.Println("j=",j)
    fmt.Println("k=",k)
    fmt.Println("l=",l)
}
//i= 1, j= 6 ,k= 12, l= 24
/**
    i左移0位, j左移1位, k左移2位, l左移4位
    
    <<n==*(2^n)
**/

运算符

Go 语言内置的运算符有:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 其他运算符

算术运算符

运算符描述实例
+相加10 + 20 = 30
-相减10 - 20 = -10
*相乘10 * 20 = 200
/相除10 / 20 = 0.5
%求余10 % 20 = 10
++自增10++ = 11
--自减10-- = 9

关系运算符

运算符描述实例
==检查两个值是否相等,如果相等返回 true 否则返回 false1 == 1 // true
!=检查两个值是否不相等,如果不相等返回 true 否则返回 false1 != 1 // false
检查左边值是否大于右边值,如果是返回 true 否则返回 false1 > 1 false
<检查左边值是否小于右边值,如果是返回 true 否则返回 false1 < 1 // false
>=检查左边值是否大于等于右边值,如果是返回 true 否则返回 false1 >= 1 // true
<=检查左边值是否小于等于右边值,如果是返回 true 否则返回 false1 <= 1 // true

逻辑运算符

运算符描述实例
&&(逻辑 AND 运算符)如果两边的操作数都是 true,则条件 true,否则为 false。true && false // false
|| (逻辑 OR 运算符)相减 false || false // false
!(逻辑 NOT 运算符)如果条件为 true,则逻辑 NOT 条件 false,否则为 true。!true // false

位运算符

位运算符对整数在内存中的二进制位进行操作 位运算符 &, |, 和 ^ 的计算如下

pqp & qp | qp ^ q
00000
01011
11110
10011
运算符描述实例
&按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。1 & 1 // 1
|按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或0 | 1 // 1
按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。0 ^ 1 // 1 , 1 ^ 1 // 0
<<左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。1 << 5 // 32
>>右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。1 >> 5 // 0

赋值运算符

运算符描述实例
=右边的值赋值到左边实例
+=相加后再赋值a += 5 相当于 a = a + 5
-=相减后再赋值a -= 5 相当于 a = a - 5
*=相乘后再赋值a *= 5 相当于 a = a * 5
/=相除后再赋值a /= 5 相当于 a = a / 5
%=求余后再赋值a %= 5 相当于 a = a % 5
<<=左移后赋值a <<= 5 相当于 a = a << 5
>>=右移后赋值a >>= 5 相当于 a = a >> 5
&=按位与后赋值a &= 5 相当于 a = a & 5
^=按位异或后赋值a ^= 5 相当于 a = a ^ 5
|=按位或后赋值a |= 5 相当于 a = a | 5

其他运算符

运算符描述实例
&返回变量存储地址实例
*指针变量&a : 变量a的内存地址
-=相减后再赋值*a

运算符优先级

优先级从上至下如下, 可以通过()改变优先级

优先级运算符
5* / % << >> & &^
4+ - | ^
3== != < <= > >=
2&&
1||

Go 语言条件语句

if

image.png

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

例子

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 10
 
   /* 使用 if 语句判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" )
   }
   fmt.Printf("a 的值为 : %d\n", a)
}
// a 小于 20
// a 的值为 : 10

if...else

image.png

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

import "fmt"

func main() {
   /* 局部变量定义 */
   var a int = 100;
 
   /* 判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" );
   } else {
       /* 如果条件为 false 则执行以下语句 */
       fmt.Printf("a 不小于 20\n" );
   }
   fmt.Printf("a 的值为 : %d\n", a);

}
// a 不小于 20
// a 的值为 : 10

if...else if

if 布尔表达式 1 {
   /* 在布尔表达式 1 为 true 时执行 */
   if 布尔表达式 2 {
      /* 在布尔表达式 2 为 true 时执行 */
   }
}
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
 
   /* 判断条件 */
   if a == 100 {
       /* if 条件语句为 true 执行 */
       if b == 200 {
          /* if 条件语句为 true 执行 */
          fmt.Printf("a 的值为 100 , b 的值为 200\n" );
       }
   }
   fmt.Printf("a 值为 : %d\n", a );
   fmt.Printf("b 值为 : %d\n", b );
}

// a 的值为 100 , b 的值为 200
// a 值为 : 100
// b 值为 : 200

switch...case

  • switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
  • switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。
  • switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。
  • switch 支持多条件匹配

image.png

示例

package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90

   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      // switch 支持多条件匹配
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }

   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )    
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" );
   }
   fmt.Printf("你的等级是 %s\n", grade );
   
// 优秀!
// 你的等级是 A

switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。

package main
import "fmt"
func main() {
   var x interface{}
     
   switch i := x.(type) {
      case nil:  
         fmt.Printf(" x 的类型 :%T",i)                
      case int:  
         fmt.Printf("x 是 int 型")                      
      case float64:
         fmt.Printf("x 是 float64 型")          
      case func(int) float64:
         fmt.Printf("x 是 func(int) 型")                      
      case bool, string:
         fmt.Printf("x 是 bool 或 string 型" )      
      default:
         fmt.Printf("未知型")    
   }  
}

// x 的类型 :<nil>

使用 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
    case true:
            fmt.Println("4、case 条件语句为 true")
    case false:
            fmt.Println("5、case 条件语句为 false")
            fallthrough
    default:
            fmt.Println("6、默认 case")
    }
}
// 2、case 条件语句为 true
// 3、case 条件语句为 false
// 4、case 条件语句为 true

select

  • select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
  • select 随机执行一个可运行的 case。会循环检测条件,如果有满足则执行并退出,否则一直循环检测(select阻塞)
  • 多个case可执行, 只执行一个,其他不执行

语言循环语句

image.png

for循环可以嵌套

go 的几种for写法

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


// 类似 for(let i = 0; i < 10; i++) {}
// for init; condition; post { }

// condition为真时执行, 类似于 while
for condition { }

// 类似于 for(;;)  无限循环
for {}

计算1-10的和

// for init; condition; post { }
package main

import "fmt";

func main() {
    sum := 0;
    for i :=0; i <= 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {
    newMap[key] = value
}

Go 语言函数

  • Go 语言最少有个 main() 函数。
  • 函数声明告诉了编译器函数的名称,返回类型,和参数。
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 声明局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

// 函数返回多个值
func swap(x, y string) (stringstring) {
   return y, x
}

// 函数调用
a, b := swap("a""b")
res := max(0, 1)

参数

参数分为值传递与引用传递. 值传递是复制到函数中,所以函数中,值改变, 实际参数不变 . 引用传递是在调用函数时候将引用函数地址传入函数, 所以, 函数参数修改, 实际参数会跟着改变

参数可以是任意值, 也可以是一个函数, 这样可以实现回调

语言变量作用域

作用域定义特点
全局变量函数外定义的变量Go 语言程序中全局变量与局部变量名称可以相同,局部变量优先级 > 全局变量
局部变量函数内定义的变量作用域只在函数体内,参数和返回值变量也是局部变量
形式参数函数定义中的变量作为局部变量使用

Go 数组

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,可以是任意类型.

image.png

声明数组

var nums [10] float32 // 数组 nums 长度为 10 类型为 float32

初始化数组

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [5]float32{1.0, 2.0, 3.0, 4.0, 5.0}

// 不知道数组的长度可以用...代替 或者不写, 编译器会根据元素个数自行推断数组的长度
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}  // 数组长为5
balance := [...]float32{1.0, 2.0, 3.0, 4.0, 5.0}  // 数组长为5
var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}  // 数组长为5
balance := []float32{1.0, 2.0, 3.0, 4.0, 5.0}  // 数组长为5

// 如果设置了数组的长度,还可以通过指定下标来初始化元素
var balance = [5]float32{1: 1.0, 2: 2.0, 3: 3.0} // 初始化索引为 1, 2, 3 的值:[0, 1, 2, 3, 0]
balance := [5]float32{1: 1.0, 2: 2.0, 3: 3.0} // [0, 1, 2, 3, 0]
balance[2] = 1.0 // [0, 1, 1, 3, 0]

访问数组元素

var a float32 = balance[1]   // 取balance索引为1的第二位元素

Go 语言指针

声明指针变量

一个指针变量会指向一个值的内存地址, 声明如下:

var ip *int //整型
var fp *float32 // 浮点型

为指针变量赋值

var ip *int = &a

拿到变量的内存地址: &

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

package main
import "fmt"
func main() {
   var a int = 10
   fmt.Printf("a的地址是: %x\n", &a) // a的地址是: c000124008
}

访问指针变量中指向地址的值: *, 指针类型前面加上 * 号(前缀)来获取指针所指向的内容

package main

import "fmt"

func main() {
    var a int = 10
    var ip *int = &a
    fmt.Printf("a 变量的地址是: %x\n", &a)
    fmt.Printf("ip 变量储存的指针地址: %x\n", ip)
    fmt.Printf("*ip 变量的值: %d\n", *ip)
}

// a 变量的地址是: c0000ae008
// ip 变量储存的指针地址: c0000ae008
// *ip 变量的值: 10

Go 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。指代空值或者零值

Go 语言结构体

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义结构体

// 定义了 一个 名为 书的结构体, 包含有title, author, subject, book_id属性
type Books struct {
   title string
   author string
   subject string
   book_id int
}

定义好的结构体可以用于声明变量

book1 := Books { "测试大全", "佚名", "软件工程", "4567" }
book2 := Books { title: "测试大全", author: "佚名", subject: "软件工程", book_id: "4567" }
// 忽略的字段为 0 或 空
book3 := Books { title: "测试大全", book_id: "4567" }

访问结构体成员

// 赋值
var Book1 Books
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407

// 取值
fmt.Println(Book1.title) // Go 语言

Go 切片

Go 语言切片是对数组的抽象。切片是Go内置类型切片("动态数组").与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

定义切片

var slice1 []string = make([]string, 5)
slice1 := make([]string, 5)

初始化

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