头图

本文将介绍 Go 语言的基础语法,包括环境配置、数据类型、流程控制、函数、结构体、接口、异常、文本处理、并发编程、网络编程等。本文是对多个 Go 入门视频的总结,并结合个人理解对内容进行了简化。由于水平有限,文中可能存在些许错误,烦请评判指正。

前言

Go 的作者:Rob Pike(罗伯·派克)、Ken Thompson(肯·汤姆森)、Robert Griesemer(罗伯特·格里茨默)。

Go 的开源时间:2009 年 11 月 10 日。

Rob Pike 曾说:“你是否认同 Go,取决于你是否认同少就是多,还是少就是少(Less is more or less is less)”。Go 的设计哲学是:将简单、使用体现得淋漓尽致。

使用 Go 的项目:Docker、Kubernetes、etcd、beego、codis。

使用 Go 的公司:Google、Faebook、腾讯、百度、七牛云、京东、小米、字节跳动。

环境配置

下载地址:All releases - The Go Programming Language (google.cn),推荐下载 zip 格式。

配置环境变量:添加以下变量,并将 %GOROOT%\bin 添加到 Path 变量中。

变量说明
GOROOTD:\Development\Environment\Go\Go-1.23.1Go 安装目录
GOPATHD:\LocalRepository\GoGo 依赖安装目录
GOPROXYhttps://mirrors.aliyun.com/goproxy/Go 依赖镜像

CMD 执行 go version 查看 Go 版本。

集成开发环境:GoLand by JetBrains: More than just a Go IDE

基础语法

注释

// 单行注释(推荐)

/*
多行注释
*/

package main


// 普通
import "fmt"

// 忽略
import _ "fmt"

// “解构”
import . "fmt"

// 别名
import ifmp "fmt"

// “分组”
import (
    "fmt"
    "os"
)

// 主函数
func main(){
    
}

一个可执行程序必须包含一个 main 包,一个 main 包必须包含一个 main 函数。

所在目录(父级)相同 go 文件的包名必须相同。

变量

变量被声明后必须被使用。

// 定义
var a int
var a, b string
var (
    a string
    b string
)
// 定义并初始化
var a string = "a"
var a, b string = "a", "b"
var a, b = "a", "b"
a, b := "a", "b"            // 自动类型推导

零值(默认值):整型为0,浮点型为0.0,字符串为空字符串,布尔型为false,切片、函数、指针默认为 nil。

变量遵循驼峰命名。对于全局变量,若首字母大写,则认定为 “public”。

交换变量的值。

a, b, c = b, c, a

匿名变量。匿名变量不占用空间,不会分配内存。

a, _ := getTwo()
_ = getOne()

变量作用域:局部变量、全局变量。全局变量与局部变量可同名。

// 全局变量
age := 14

func main(){
    // 局部变量
    age := 15
    fmt.Println(age)    // 15
}

常量。常量的定义和初始化与变量类似。

// 单个常量
const a = 1

// 多个常量
const a, b = 1, 2
const (
    b = 2
    c = 3
)

// iota
const (
    a = iota    // 0
    b             // 1
    c = "c"        // c
    d            // c
    e = 100        // 100
    f            // 100
    g = iota    // 6
    h            // 7
)
const (
    i = iota    //0
    j            // 1
)

数据类型

布尔型:默认 false。

 var flag = true

整型。字符用 int32 存储。

类型描述
uint8 / byte0 到 2^8
uint160 到 2^16
uint32 / uint(32位系统)0 到 2^32
uint64 / uint(64位系统)0 到 2^64
int8-2^7 到 2^7-1
int16-2^15 到 2^15-1
int32 / rune / int(32位系统)-2^31 到 2^31-1
int64 / int(64位系统)-2^63 到 2^64-1

浮点型。

类型描述
float32IEEE-754 32 位浮点数
float64IEEE-754 64 位浮点数

字符串。

var s string = "tom"
fmt.Println(s[0])

类型转换。

// 数值转数值
b := float64(a)

// 转字符串
s := strconv.FormatInt(int64(v), 10)
s := strconv.Itoa(v)    // 特殊

// 字符串转数值
v, _ := strconv.ParseInt(s, 10)
v, _ := strconv.Atoi(s)    // 特殊

指针:无需手动回收,内存回收由 GC 自动完成。

var p *int        // 默认nil
var p = &a
var p = new(int)

var b = *p

数组:数组大小必须为常量。

var arr [5]int
var arr [5]int = [5]int{1, 2}    // 未初始化的值默认为0
var arr = [5]int{1, 2, 3, 4}
arr := [5]int{1, 2, 3, 4}

a := [3]int{1, 2, 3}
b := [3]int{1, 2, 3}
fmt.Println(a == b)        // true

切片:自动扩容。

var a []int
var a []int = []int{1, 2, 3}
var a = []int{1, 2, 3}
a := []int{1, 2, 3}
a := make([]int, 10, 15)    // 类型 长度 容量

// 长度
len(a)
// 容量
cap(a)

// 追加
a = append(a, 1)
a = append(a, b...)

// 截取
b := a[1:2:1]    // [low:high:max] 左边右开 长度=high-low 容量=max-high
b := a[1:2]
b := a[:2]
b := a[1:]

// 数组切片-浅拷贝
a := []int{1, 2, 3}
b := a[1:2]
b = append(b, 4)
b[0] = 5
fmt.Println(a)    // 1 5 4
fmt.Println(b)    // 5 4

map。

var mp map[string]int
var mp = map[string]int{"a": 1, "b": 2}
mp := make(map[string]int, 10)

运算符

算术运算符

+    -    *    /    %    ++    --

关系运算符

==    !=    >    >=    <    <=

逻辑运算符

&&    ||    !

位运算符:a &^ b,将 a 中与 b 中 1 的对应位置 0,称为位清空。

&    |    ^    &^    <<    >>    

赋值运算符

=    +=    -=    *=    /=    %=    <<=    >>=    &=    ^=    |=

输入输出

fmt.Println(1)
fmt.Printf("%d", 2)
fmt.Print(3)

fmt.Scan(&age)
fmt.Scanln(&age)
fmt.Scanf("%s", &age)

流程控制

if-else

if age < 18 {

} else if age < 35 {

} else {

}
// 含初始化语句
if a := 10; a >= 10 {

} else if b := 20; b >= 20 {

}else {}

switch:fallthrough、break。

// 执行匹配项
switch age {
    case 18:{}
    case 35, 45:{}
    default:{}
}
// 执行最后一个匹配项
switch {
    case age < 18:{}
    case age < 35:{}
    default:{}
}
// 执行true
switch {
    case true:{}
    case false:{}
    default:{}
}
// fallthrough执行下一个case
// break跳出switch
switch idx {
    case 1:
        fallthrough
    case 2:
        break
        fallthrough
    default:
}
// 判断类型
switch x.(type) {
    case string:
    case int:
    default:
}

for:break、continue。

for i := 0; i < 10; i++ {}
i := 0
for ; i < 10; i++ {}
i := 0
for i < 10 { i++ }
for { }
for i := range s {}
for i, ch := range s { }

goto

func f() {
    goto L1
    fmt.Println(1)
L1:
    fmt.Println(2)
}

函数

func say(a, b string) (c, d string) {
    return b, a
}
func main() {
    e, f := say("a", "b")
}
// 可变参数
func say(cnt int, args ...string) {
    fmt.Println(cnt)
    for _, v := range args {
        fmt.Println(v)
    }
}
// 匿名函数(立即执行)
func main() {
    func() {
        fmt.Println("s")
    }()
}

参数传递

  • 值传递:int、float64、string、bool、array等。
  • 引用传递:slice、map、chan等。
// 执行顺序:d、e、c、b、a,defer逆序执行
func main() {
    defer a()
    defer b()
    defer c()
    d()
    e()
}
// 函数类型
var f  func(int,int)
var f = func(int, int) { }
// 回调函数
func f1(a, b int) int {
    return a + b
}
func f2(a, b int, f func(int, int) int) int {
    return f(a, b)
}
// 闭包
func main() {
    fc := f()
    fmt.Println(fc()) // 1
    fmt.Println(fc()) // 2

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

main 函数:一个可执行程序必须包含一个 main 包,一个 main 包必须包含一个 main 函数,仅 main 包下的 main 可作为入口函数。

package main

func main() {
    
}

init 函数:在导入一个包的同时,会直接执行这个包的 init(),main 包的 init() 会先于 main() 执行。

内置函数。

len()
cap()

a = append(a, 1)
a = append(a, b...)

a := []int{1, 2, 3}
b := []int{4, 5}
copy(a, b)
fmt.Println(a)    // 4 5 3
fmt.Println(b)    // 4 5

delete(a, 1)

编码规范

导包。

import (
    "fmt"
    "json"
)

命名:一个目录一个 go 文件,go 文件包名与目录名保持一致,包名与目录名采用小写,多个单词用下划线分隔。

结构体

type Student struct {
    id   int
    name string
}
func main() {
    // 按顺序赋值
    var s1 = Student{1, "tom"}
    // 指定字段赋值
    var s2 = Student{
        id:   1,
        name: "tom",
    }
    // 指针
    s3 := &s1
    s4 := new(Student)
    
    fmt.Println(s1.id, s2.name)
}
type Student struct {
    id   int
    // 首字母大写才可跨包可见(public)
    Name string
}

继承

// 结构体匿名变量
type Person struct {
    id int
}
type Student struct {
    Person
    name string
}

func main() {
    s := Student{Person{1}, "tom"}
    s.Person= Person{2}
    s.Person.id = 1
    s.id = 2
    s.name = "jack"
}
// 结构体指针匿名变量
type Person struct {
    id int
}
type Student struct {
    *Person
    name string
}

func main() {
    s := Student{&Person{1}, "tom"}
}
// 同名变量
type Person struct {
    id int
}
type Student struct {
    *Person
    id int
}

func main() {
    s := Student{&Person{1}, 2}
    fmt.Println(s.id)                // 2
}
// 非结构体类型匿名变量
type Student struct {
    int
}

func main() {
    s := Student{1}
    fmt.Println(s.int)
}

方法

type Student struct {
    id int
}
// 方法挂载到自定义类型
func (s Student) say() {
    // s是实参的拷贝
    fmt.Println(s.id)
}
func main() {
     Student{1}.say()
}
type str string
// 方法挂载到自定义类型
func (s str) say() {
    fmt.Println(s)
}
func main() {
    s := str("hi")
    s.say()
}

方法集:指针变量和非指针变量可看作共用方法集(语法糖)

type Student struct {
    age int
}
func (s *Student) add(v int) {
    s.age += v
}
func main() {
    s := Student{1}
    s.add(1)
    fmt.Println(s.age)    // 2
}

方法的继承。

type Person struct {
    id int
}
type Student struct {
    Person
}

func (p *Person) say() {
    fmt.Println(p.id)
}
func main() {
    s := Student{Person{1}}
    s.say()
}

方法的重写。

type Person struct {
    id int
}
type Student struct {
    Person
}

func (p *Person) say() {
    fmt.Println("p")
}

func (s *Student) say() {
    fmt.Println("s")
}
func main() {
    s := Student{Person{1}}
    s.say()            // s
    s.Person.say()    // p
}

方法值。

p := Person{1}
pf := p.say
pf()

方法表达式。

p := Person{1}
pf := (*Person).say
pf(&p)

接口

若一个类型实现了一个接口的所有方法,便认为该类型体实现了该接口。

type Person interface {
    say()
}
type Student struct {
}

func (s *Student) say() {
    fmt.Println("Student")
}

func main() {
    var p Person = new(Student)
    // 多态
    p.say()
}

接口的继承

type Person interface {
    say()
}
type GoodPerson interface {
    Person
}
type Person interface {
    say()
}
type GoodPerson interface {
    Person
    sayGood()
}
type Student struct {}

func (s *Student) say() {}
func (s *Student) sayGood() {}

func main() {
    var p Person = &Student{}
    var gp GoodPerson = &Student{}
    // 下转上
    p = gp
    // 上转下 不允许
    // gp = p

空接口

可以将任何类型的值赋给空类型的变量。

var v interface{}
v = &Student{}

类型断言。

// 断言v是Person类型
// ok=true表示断言成功,否则断言失败
// 若断言成功,则会将v转为Person类型并赋给sv,否则sv为零值
if sv, ok := v.(Person); ok {
    sv.say()
}
func main() {
    var v interface{}
    v = &Student{}    // Student实现了Person
    switch v.(type) {
        case int:
            fmt.Println("int")
        case Student:
            fmt.Println("Student")
        case Person:
            fmt.Println("Person")
        default:
            fmt.Println("default")
    }
}
// 输出Person

异常

内置 error 接口。

// builtin.go
type error interface {
    Error() string
}

error 接口的实现。

// errors.go
type errorString struct {
    s string
}
func (e *errorString) Error() string {
    return e.s
}

使用。

func main() {
    err := fmt.Errorf("%s", "error msg")
    fmt.Println(err)
}
func main() {
    err:=errors.New("error msg")
    fmt.Println(err)
}
func div(a, b int) (res int, err error) {
    if b == 0 {
        err = errors.New("divide by zero")
    } else {
        res = a / b
    }
    return
}
func main() {
    res, err := div(10, 0)
    fmt.Println(res, err)
}

当发生致命错误时,可调用 panic 函数中断程序。

func div(a, b int) (res int, err error) {
    if b == 0 {
        panic("divide by zero")
    }
    res = a / b
    return
}

recover 函数可拦截异常。

func div(a, b int) (res int, err error) {
    defer func() {
        if e := recover(); e != nil {
            fmt.Println(e)
        }
    }()
    if b == 0 {
        panic(errors.New("divide by zero"))
    }
    res = a / b
    return
}

文本处理

字符串操作。

strings.Contains()
strings.Join()
strings.Index()
strings.Repeat()
strings.Replace()
strings.Split()
strings.Trim()
strings.Fields()

字符串转换。

// 其他类型转string
strconv.FormatXXX()
// string转其他类型
strconv.ParseXXX()

正则表达式。

func main() {
    r, s := ".*", "abc"
    reg := regexp.MustCompile(r)
    reg.Match([]byte(s))
    reg.FindStringIndex(s)
}

JSON。

type Student struct {
    Id   int    `json:"id"`
    Name string `json:"-"`
    Flag bool   `json:"flag,string"`
}

func main() {
    s := Student{1, "tom", true}
    js, _ := json.Marshal(s)
    _ = json.Unmarshal(js, &s)
}

文件操作

创建/打开文件

func Create(name string) (*File, error)
func Open(name string) (*File, error) 
func OpenFile(name string, flag int, perm FileMode) (*File, error)

写文件

func WriteFile(name string, data []byte, perm FileMode) error

读文件

func ReadFile(name string) ([]byte, error) 

删除文件

func Remove(name string) error
func RemoveAll(path string) error 

命令行参数

os.Args

并发编程

协程

func main() {
    go func() {
        
    }()
}
// 设置CPU核数
runtime.GOMAXPROCS(1)
// 当前协程让出时间片
runtime.Gosched()
// 退出当前协程
runtime.Goexit()

channel

goroutine奉行通过通信来共享内存,而不是通过共享内存来通信。

// 通道-无缓冲
var ch = make(chan int)

func main() {
    go func() {
        // 存放数据,阻塞等待
        ch <- 1
    }()
    // 阻塞等待,获取数据
    d := <-ch
    fmt.Println(d)
}
// 通道-有缓冲
var ch = make(chan int, 2)

func main() {
    cnt := 3
    go func() {
        for i := 0; i < cnt; i++ {
            // 存放数据,缓存区满时阻塞等待
            ch <- 1
        }
    }()
    for i := 0; i < cnt; i++ {
        // 阻塞等待数据
        d := <-ch
        fmt.Println(d)
    }
}
// 关闭通道
func main() {
    ch := make(chan int, 2)
    go func() {
        for i := 0; i < 3; i++ {
            ch <- 1
        }
        close(ch)
    }()
    for {
        if v, ok := <-ch; ok {
            fmt.Println(v)
        } else {
            break
        }
    }
}
func main() {
    ch := make(chan int, 2)
    go func() {
        for i := 0; i < 3; i++ {
            ch <- 1
        }
        close(ch)
    }()
    for d := range ch {
        fmt.Println(d)
    }
}
// 双向 单向
func main() {
    // 双向
    ch := make(chan int, 2)
    // 单向只读
    var rch <-chan int = ch
    // 单向只写
    var wch chan<- int = ch
}
// 通道参数
func main() {
    ch := make(chan int, 2)
    go read(ch)
    go write(ch)
}

func read(rch <-chan int) {}
func write(wch chan<- int) {}

定时器

// 等待5s
timer := time.NewTimer(5 * time.Second)
<-timer.C    

// 等待1s
time.Sleep(1 * time.Second)

// 等待5s
time.After(5 * time.Second)
timer :=time.NewTimer(time.Second)
// 重设定时器
timer.Reset(time.Minute)
// 停止定时器
timer.Stop()
// 定时执行
ticker := time.NewTicker(2 * time.Second)
for i := 0; ; i++ {
    <-ticker.C
    fmt.Println(time.Now().Unix())
    if i >= 10 {
        ticker.Stop()
        break
    }
}
// select
func main() {
    c1, c2 := make(chan int), make(chan int)
    go func() {
        x, y := 1, 2
        for i := 0; i < 10; i++ {
            select {
            // 写数据
            case c1 <- x:
                fmt.Println("c1", i, x)
                x, y = y, x
                // 读数据
            case k := <-c2:
                fmt.Println("c2", i, k)
            }
        }
        // 对于每轮循环,select会随机选择一个未阻塞的case来执行
        // 如果所有case都阻塞,select会阻塞 
    }()
    for i := 0; i < 3; i++ {
        // 读数据
        <-c1
        // 写数据
        c2 <- i
    }
}

// 超时取消
go func() {
label:
    for {
        select {
        case v := <-ch:
            fmt.Println(v)
        case <-time.After(time.Second * 5):
            fmt.Println("timeout")
            break label
        }
    }
}()

网络编程

Scoket编程

服务端。

func main() {
    // 监听
    listener, _ := net.Listen("tcp", ":8080")
    defer listener.Close()
    // 处理请求
    for {
        conn, _ := listener.Accept()
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    buf := make([]byte, 1024)
    n, _ := conn.Read(buf)
    fmt.Println(string(buf[:n]))
    _ = conn.Close()
}

客户端。

func main() {
    conn, _ := net.Dial("tcp", ":8080")
    // 连接服务器
    defer conn.Close()
    // 发送数据
    _, _ = conn.Write([]byte("Hi"))
}

Http编程

服务端。

func main() {
    // 处理请求
    http.HandleFunc("/", handleConn)
    // 监听
    _ = http.ListenAndServe(":8080", nil)
}

func handleConn(w http.ResponseWriter, req *http.Request) {
    _, _ = w.Write([]byte("Hi"))
}

客户端。

func main() {
    resp, _ := http.Get("https://www.baidu.com")
    defer resp.Body.Close()
    buf := make([]byte, 10240)
    n, _ := resp.Body.Read(buf)
    fmt.Println(string(buf[:n]))
}

END

以上就是本文的全部内容,文档会根据自己的实际使用和各位提出的问题而不断更新。

如果觉得本文对您有一点点帮助,欢迎点赞、转发加关注,这会对我有非常大的帮助,如果有任何问题,欢迎在评论区留言,咱们下期见!


字节幺零二四
9 声望5 粉丝

talk is cheap, show me you code!