这是我参与「第五届青训营」伴学笔记创作活动的第 1 天
什么是Go语言?
诞生于高并发场景
Go于2009年发布,当时多核处理器已经上市。Go语言在多核并发上拥有原生的设计优势,Go语言从底层原生支持并发,无需第三方库。Go语言的并发是基于goroutine的,goroutine 类似于线程,但并非线程。
性能强悍
比较好学
学习曲线平缓,和C/C++相比还是非常友好的
Go语言前景
应用Go语言的公司举例:
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,需要使用内建函数
make:make(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 握手流程 使流量通过代理服务器