本文将介绍 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
变量中。
变量 | 值 | 说明 |
---|---|---|
GOROOT | D:\Development\Environment\Go\Go-1.23.1 | Go 安装目录 |
GOPATH | D:\LocalRepository\Go | Go 依赖安装目录 |
GOPROXY | https://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 stringb 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 := 14func main(){// 局部变量age := 15fmt.Println(age) // 15
}
常量。常量的定义和初始化与变量类似。
// 单个常量
const a = 1// 多个常量
const a, b = 1, 2
const (b = 2c = 3
)// iota
const (a = iota // 0b // 1c = "c" // cd // ce = 100 // 100f // 100g = iota // 6h // 7
)
const (i = iota //0j // 1
)
数据类型
布尔型:默认 false。
var flag = true
整型。字符用 int32 存储。
类型 | 描述 |
---|---|
uint8 / byte | 0 到 2^8 |
uint16 | 0 到 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 |
浮点型。
类型 | 描述 |
---|---|
float32 | IEEE-754 32 位浮点数 |
float64 | IEEE-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:fallthroughcase 2:breakfallthroughdefault:
}
// 判断类型
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 L1fmt.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()) // 1fmt.Println(fc()) // 2}
func f() func() int {i := 0return func() int {i++return i}
}
main 函数:一个可执行程序必须包含一个 main 包,一个 main 包必须包含一个 main 函数,仅 main 包下的 main 可作为入口函数。
package mainfunc 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 5delete(a, 1)
编码规范
导包。
import ("fmt""json"
)
命名:一个目录一个 go 文件,go 文件包名与目录名保持一致,包名与目录名采用小写,多个单词用下划线分隔。
结构体
type Student struct {id intname string
}
func main() {// 按顺序赋值var s1 = Student{1, "tom"}// 指定字段赋值var s2 = Student{id: 1,name: "tom",}// 指针s3 := &s1s4 := new(Student)fmt.Println(s1.id, s2.name)
}
type Student struct {id int// 首字母大写才可跨包可见(public)Name string
}
继承
// 结构体匿名变量
type Person struct {id int
}
type Student struct {Personname string
}func main() {s := Student{Person{1}, "tom"}s.Person= Person{2}s.Person.id = 1s.id = 2s.name = "jack"
}
// 结构体指针匿名变量
type Person struct {id int
}
type Student struct {*Personname string
}func main() {s := Student{&Person{1}, "tom"}
}
// 同名变量
type Person struct {id int
}
type Student struct {*Personid 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() // ss.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 {PersonsayGood()
}
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实现了Personswitch 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 / breturn
}
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 / breturn
}
文本处理
字符串操作。
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 := <-chfmt.Println(d)
}
// 通道-有缓冲
var ch = make(chan int, 2)func main() {cnt := 3go func() {for i := 0; i < cnt; i++ {// 存放数据,缓存区满时阻塞等待ch <- 1}}()for i := 0; i < cnt; i++ {// 阻塞等待数据d := <-chfmt.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.Cfmt.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, 2for 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
以上就是本文的全部内容,文档会根据自己的实际使用和各位提出的问题而不断更新。
如果觉得本文对您有一点点帮助,欢迎点赞、转发加关注,这会对我有非常大的帮助,如果有任何问题,欢迎在评论区留言,咱们下期见!