Go语言介绍+语法基础+实战 | 青训营笔记

90 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 1 天

pexels-joyston-judah-933054.jpg

什么是Go语言?

诞生于高并发场景

Go于2009年发布,当时多核处理器已经上市。Go语言在多核并发上拥有原生的设计优势,Go语言从底层原生支持并发,无需第三方库。Go语言的并发是基于goroutine的,goroutine 类似于线程,但并非线程。

性能强悍

比较好学

学习曲线平缓,和C/C++相比还是非常友好的

Go语言前景

应用Go语言的公司举例: image-20190203135218456.png

Go基础语法

IDE选择

VSCode 或 GoLand

新手建议GoLand, VSCode拓展性强

打印传说中的“hello world”

package main //main包的一部分,程序的入口包(入口文件)

import (
	"fmt" //导入fmt包,作用:格式化输入输出
)

func main() {
	fmt.Println("hello world")
}

在 Go 语言中,;不是必要的,当一行中只存在一个语句时,则不必显式的为语句末添加 ;

要运行这个程序,先将将代码放到名为 hello-world.go 的文件中,然后执行 go run

go build 可以将程序编译成二进制文件.exe

值与变量

字符串可以通过 + 连接

fmt.Println("go" + "lang") //golang

整数和浮点数

fmt.Println("1+1 =", 1+1) //1+1 = 2
fmt.Println("7.0/3.0 =", 7.0/3.0) //7.0/3.0 = 2.3333333333333335

布尔型,以及常见的布尔操作。

fmt.Println(true && false) //false
fmt.Println(true || false) //true
fmt.Println(!true) //false

一个或多个变量 可以一次性声明

var a = "initial"
var b, c int = 1, 2 

声明后却没有给出对应的初始值时,变量将会初始化为 零值 。 例如,int 的零值是 0

var e int
fmt.Println(e) // 0

:= 语法是声明并初始化变量的简写, 例如 var f string = "short" 可以简写为这样

f := "short"
fmt.Println(f)

var和const可以出现在函数外 数值型常量没有确定的类型,直到被给定某个类型,比如显式类型转化。 一个数字可以根据上下文的需要(比如变量赋值、函数调用)自动确定类型。

package main

import (
    "fmt"
    "math"
)

const s string = "constant"

func main() {
    fmt.Println(s) //constant

    const n = 500000000

    const d = 3e20 / n
    fmt.Println(d) //6e+11

    fmt.Println(int64(d)) //60000000000

    fmt.Println(math.Sin(n)) //-0.28470407323754404
}

流程控制

For循环

最基础的方式,单个循环条件

i := 1
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }

经典的初始/条件/后续 for 循环

for j := 7; j <= 9; j++ {
        fmt.Println(j)
    }

不带条件的 for 循环将一直重复执行, 直到在循环体内使用了 break 或者 return 跳出循环。

for {
        fmt.Println("loop")
        break
    }

continue直接进入下一次循环

for n := 0; n <= 5; n++ {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }

If/Else分支

注意,在 Go 中,条件语句的圆括号不是必需的,但是花括号是必需的。Go 没有三目运算符, 即使是基本的条件判断,依然需要使用完整的 if 语句。

package main
import "fmt"
func main() {

    if 7%2 == 0 {
        fmt.Println("7 is even")
    } else {
        fmt.Println("7 is odd")
    }

    if 8%4 == 0 {
        fmt.Println("8 is divisible by 4")
    }

    if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}

Switch 分支

switch语句中的变量可以是任何类型,可以是一个常量或者一个变量,在每个case后面可以跟多个值,用逗号隔开

Go中的switch语句也支持在条件判断语句前执行简单的语句,这样就可以在一个switch语句中处理多个条件

不使用break 且不会像c一样继续向下执行,而是直接跳出


import (
	"fmt"
	"time"
)

func main() {

	a := 4
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	//多个值用逗号隔开
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}

数组、切片、Map和range

数组array

在 Go 中,数组 是一个具有编号且长度固定的元素序列。 与其他语言几乎相同,但定义方式需注意。

var a [5]int

fmt.Println("len:", len(a)) //内置函数len可以返回数组的长度。

b := [5]int{1, 2, 3, 4, 5}

切片slice

Slice 是 Go 中一个重要的数据类型,它提供了比数组更强大的序列交互方式。

数组是定长的,切片是可变容量的,工作原理类似于Java的ArrayList,也会自动扩容。

声明一个切片:

s := make([]string, 3)

slice支持内建函数append,返回一个新的slice (需要接收)

s = append(s, "d")

slice 还可以 copy,创建一个空的和 s 有相同长度的 slice——c, 然后将 s 复制给 c

c := make([]string, len(s))
    copy(c, s)

slice 支持通过 slice[low:high] 语法进行“切片”操作。 例如,下面可以得到一个包含元素 s[2]s[3] 和 s[4] 的 slice。

l := s[2:5]

这个 slice 包含从 s[0] 到 s[5](不包含 5)的元素。

l = s[:5]

同理,从 s[2](包含 2)之后的元素。

l = s[2:]

关于更多关于slice的细节设计可以看看官方的博文

映射Map

map 是 Go 内建的关联数据类型

map遍历时完全无序的,不会按照字母或者插入顺序

要创建一个空 map,需要使用内建函数

 makemake(map[key-type]val-type)

提前声明一个新的map

n := map[string]int{"foo": 1, "bar": 2}

设置键值对name[key] = val

获取一个键的值name[key]

内建函数 len 可以返回一个 map 的键值对数量。

内建函数 delete 可以从一个 map 中移除键值对

当从一个 map 中取值时,还有可以选择是否接收的第二个返回值(布尔型),该值表明了 map 中是否存在这个键。 这可以用来消除 键不存在键的值为零值 产生的歧义, 例如 0 和 ""。

val, ok := m["k2"]

_, ok := m["k2"]  //不需要值,所以用 空白标识符(blank identifier)  _ 将其忽略

打印map将以map[k:v k:v] 的格式输出

 n := map[string]int{"foo": 1, "bar": 2}
 fmt.Println("map:", n) // map: map[foo:1 bar:2]

range遍历

range在数组和slice中提供每项索引和值的访问

    nums := []int{2, 3, 4}
    sum := 0
for index, num := range nums {
        sum += num
    }

遍历map时 迭代键值对 也可以只遍历key

kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    for k := range kvs {
        fmt.Println("key:", k)
    }

函数、指针、结构体、结构体方法

函数

函数是Go的核心 它不会自动return最后一个表达式的值


    return a + b
}

func plusPlus(a, b, c int) int {
    return a + b + c
}

它也可以设置多个返回值

func vals() (int, int) {
    return 3, 7
}

变参函数

    fmt.Print(nums, " ")
    total := 0
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}

匿名函数

其实就是把这个匿名函数的返回值当做intSeq的返回值

func intSeq() func() int {
    i := 0
    return func() int {
        i++
        return i
    }
}

我们调用 intSeq 函数,将返回值(一个函数)赋给 nextInt。 这个函数的值包含了自己的值 i,这样在每次调用 nextInt 时,都会更新 i 的值。

func main(){
    nextInt :=intSeq()

    fmt.Println(nextInt()) \\1
    fmt.Println(nextInt()) \\2
    fmt.Println(nextInt()) \\3

}

指针

Go 支持指针, 允许在程序中通过 引用传递 来传递值和数据结构。和C语言指针类似

通过 &i 语法来取得 i 的内存地址,即指向 i 的指针。

结构体

Go 的结构体(struct)  是带类型的字段(fields)集合。

定义结构体

type person struct {
    name string
    age  int
}

初始化一个结构体元素

s := person{name: "Sean", age: 50}

使用.来访问结构体字段

fmt.Println(s.name)

结构体方法

结构体的方法定义在结构体外

需要在方法名前加上(r,rect) 表示是一个拥有rect类型接收器(receiver)的方法

type rect struct {
    width, height int
}
func (r rect) perim() int {
    return 2*r.width + 2*r.height
}

Go语言实战小项目

猜数游戏

给定随机数,用户输入猜测数,给出大小相比结果

在线词典

通过HTTP爬虫爬取其他词典网站翻译接口的引擎返回翻译结果

SOCKS5代理

简单实现SOCKS 5 握手流程 使流量通过代理服务器