一、前言
go语言因为其高[并发编程]优势,很多大厂后端技术栈都在切go,我们公司最近项目也用到go语言。本文介绍go语言的基本语法和核心特性,方便长期未使用go编程后可以进行快速回顾。
二、go基本语法
下表是go语言和c语言对比,语法快速自查对比表,用于快速掌握基本语法差异。
| 语法概念 | C语言 | go语言 |
|---|---|---|
| 行尾标识 | ; | 无,换行符为语句结束 |
| 格式化打印 | printf("love you %d year", 10000); | fmt.Printf("%d, %v\n", i, person) |
| 变量 | int a = 0; | 显示指定类型 var a int 类型推到a := 5 |
| 变量类型 | 强类型 | 弱类型 |
| 变量赋值 | a = 100; | a = 10 或者 a := 10 |
| 变量空值 | NULL | 对象的空值(nil) |
| 变量生命周期 | 栈变量: 函数内; 堆变量:全局 | 和C一样,全局变量和局部变量 |
| 函数定义 | int func(int a) | func ParseCityList(contents []byte) engine.ParserResult {} 返回值和变量类型在后面 |
| 函数体格式 | {} | {} |
| 函数返回值 | 单个返回值,可以返回指针 | 可以返回多个返回值,return a, b 调用者可以用_忽略返回值。 e, _, _ := func_a() |
| 逻辑运算符 | 无特殊 | 同C语言 |
| 分支条件 | if (!ok){}; switch-case | if time < 20 { x="Good day"; } 无括号 |
| 循环语句 | for(i=0;i<10;i++) {} | for i := 1; i <= 9; i++ {} |
| 面向对象支持 | 不支持 | 支持,1、无继承,使用组合和ductyping来支持。2、使用interface来实现多态 |
| 类定义 | struct{int a;int b;} | type ParserResult struct { Requests []Request Items []interface{} } 方法不用在struct内部声明,直接定义使用绑定。func (p point) print() {} |
| self指针传递 | 传递struct指针 | 自动传递 |
| 构造函数定义 | 无 | 无 |
| 继承 | 无,手动复制struct | 无,父结构组合获得方法。 |
| 线程和协程 | pthread_create | go func() |
| GC机制 | 无 | 有 |
| 泛型 | 不支持 (宏,C++支持template) | 支持 |
| 元编程 | 宏,#define | 支持,reflect包 |
| 函数式编程 | 不支持 | 支持,如func(){fmt.println("hello")}() |
| 闭包 | 支持,static变量实现。 | 支持 |
三、go核心特性
3.1 基本特性
3.1.1 切片
go语言支持切片功能,切片是可以动态取数组任意范围的内容。
go
numbers := []int{0,1,2,3,4,5,6,7,8}
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
AI写代码go
运行
123
3.1.2 map
map是key-value的映射对,key值可以是任意类型,go语言中使用案例,可以使用make或者map[关键字]构造。
go
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
// 获取键值对
v1 := m["apple"]
AI写代码go
运行
123456789101112
3.1.3 go协程
go中异步执行的协程(goroutine)非常方便,使用go语句即可异步处理,而且非常轻量化。
go
/*起go协程,异步执行*/
go func_a()
AI写代码go
运行
12
- 使用wg.Wait等待所有协程退出
go
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Goroutine 完成时调用 Done()
fmt.Printf("Worker %d started\n", id)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 增加计数器
go worker(i, &wg)
}
wg.Wait() // 等待所有 Goroutine 完成
fmt.Println("All workers done")
}
AI写代码go
运行
123456789101112131415161718192021222324
3.1.4 channel
channel是用于goroutine之间通信的通道。
- 使用make创建一个channel,定义传递的消息结构的类型。
go
ch := make(chan int)
AI写代码
1
- 消息发送接收
go
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
AI写代码
123
- select 语句
c语言中的select通常用于监听socket等fd的变化,而go中的select是用于监听所有channel的消息,并进行处理。
go
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
AI写代码go
运行
123456789101112
3.1.5 defer延迟调用
defer延迟调用在函数退出时调用,所以一些close文件、释放锁等操作可以在open时就写上defer语句,能够有效防止泄露。
go
func readFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
// 确保文件在函数返回时关闭
defer f.Close()
// ... 处理文件 ...
return nil
}
AI写代码go
运行
123456789101112
3.1.6 异常处理
go中没有try-catch的异常处理,和c语言一样需要显示的判断返回值ret,来做异常处理。
3.1.7 其他一些tips
1)常量定义用const ( a = 10)
2)类型系统: 类型推导 a := 10 和var a=10
3.2 高级特性
3.2.1 OOP编程
go语言中没有天然支持OOP,OOP(面向对象编程)的3大特性:
- 封装
- 继承
- 多态
封装就是结构体,go语言当然是支持的,多态使用interface实现,而继承go语言无法天然支持,需要通过结构体组合的方式实现。
| OOP特性 | go实现 |
|---|---|
| 封装 | struct |
| 继承 | 使用struct组合 |
| 多态 | interface+ducktyping |
类方法定义
在 Go 语言中,结构体方法是指作用于特定类型变量的函数。方法与函数的区别在于,方法具有接收者,而函数没有,接收者可以是结构体类型或非结构体类型,也可以是指针类型和非指针类型。
- 定义方法
定义方法的语法格式如下:
scss
func (接收者变量 接收者类型) 方法名(参数列表) (返回值列表) {
// 方法体
}
AI写代码
123
例如,定义一个 point 结构体及其方法,在这个例子中,print 方法作用于 point 类型的变量 p。
go
type point struct {
X int
Y int
}
func (p point) print() {
fmt.Println(p.X, p.Y)
}
AI写代码go
运行
123456789
- 指针接收者
使用指针接收者可以避免复制大对象,并且允许方法修改接收者的值。例如:
go
func (p *point) ScaleBy(factor int) {
p.X *= factor
p.Y *= factor
}
AI写代码go
运行
1234
继承:struct组合
Go 语言不支持继承和方法重载,但可以通过结构体组合,通过不同的接收者类型实现方法的重写。如下例子中,Dog 结构体重写了 Animal 结构体的 Speak 方法。
go
type Animal struct {
Name string
}
func (a *Animal) Speak() {
fmt.Println(a.Name, "says: ...")
}
type Dog struct {
Animal
}
func (d *Dog) Speak() {
fmt.Println(d.Name, "says: Woof woof!")
}
AI写代码go
运行
123456789101112131415
多态:interface
在 Go 语言中,方法是接口的实现条件之一。一个类型只有实现了接口定义的所有方法,才能被称为该接口类型的实现类。在如下这个例子中,Dog 和 Cat 类型都实现了 Animal 接口中的 Speak 方法。
go
/*定义接口*/
type Animal interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct{}
func (c Cat) Speak() string {
return "Meow!"
}
AI写代码go
运行
12345678910111213141516
- 类型断言
检查接口值是否有实现,如果没有实现则直接panic挂起。
go
value, ok := interfaceValue.(Type)
AI写代码
1
interfaceValue是一个接口类型的变量。Type是你想要断言的类型。value是断言成功后的具体类型的值。ok是一个布尔值,表示断言是否成功。
示例:
go
package main
import "fmt"
func main() {
var i interface{} = "Hello, Go!"
// 直接断言为 string 类型
s := i.(string)
fmt.Println("断言成功:", s)
// 直接断言为 int 类型(会引发 panic)
n := i.(int)
fmt.Println("断言成功:", n)
}
AI写代码go
运行
123456789101112131415
3.2.2 泛型
泛型是 Go 语言在 1.18 版本中引入的重要特性,泛型有如下概念:
| 概念 | 作用 | 示例 |
|---|---|---|
| 类型参数 | 在函数或类型名后声明,表示待定的类型。 | [T any] |
| 类型约束 | 定义类型参数必须满足的条件(如支持的操作符或方法)。 | int,float64,comparable,constraints.Ordered,any |
any |
约束类型参数为任何类型。 | [T any] |
comparable |
约束类型参数为可比较的类型。 | [K comparable] |
泛型的例子:
css
// 一个函数处理多种类型
func Max[T comparable](a, b T) T {
if a > b {
return a
}
return b
}
AI写代码
1234567
- 联合约束
除了基础的约束条件,还可以支持使用联合体的约束,如下是约束传进来的类型只能是数字。
go
// 数字类型约束
type Number interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
func Add[T Number](a, b T) T {
return a + b
}
// 使用示例
fmt.Println(Add(10, 20)) // 输出: 30
fmt.Println(Add(3.14, 2.71)) // 输出: 5.85
AI写代码go
运行
1234567891011121314
- 结构体类型的泛型
go
// 泛型栈实现
type Stack[T any] struct {
elements []T
}
// 入栈
func (s *Stack[T]) Push(value T) {
s.elements = append(s.elements, value)
}
// 出栈
func (s *Stack[T]) Pop() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
lastIndex := len(s.elements) - 1
value := s.elements[lastIndex]
s.elements = s.elements[:lastIndex]
return value, true
}
// 查看栈顶元素
func (s *Stack[T]) Peek() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
return s.elements[len(s.elements)-1], true
}
// 判断栈是否为空
func (s *Stack[T]) IsEmpty() bool {
return len(s.elements) == 0
}
// 使用示例
func main() {
// 整数栈
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
fmt.Println(intStack.Pop()) // 输出: 3 true
// 字符串栈
stringStack := Stack[string]{}
stringStack.Push("hello")
stringStack.Push("world")
fmt.Println(stringStack.Pop()) // 输出: world true
}
AI写代码go
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
3.2.3 函数式编程
go语言天生支持函数是编程,函数是一等公民,常见的如下匿名函数,异步执行:
go
go func(){
fmt.println("hello word")
}()
AI写代码go
运行
123
3.2.4 闭包
go中支持闭包,闭包为介于结构体和函数之间的对象,为函数增加context,轻量化结构,通常有2个作用:静态变量,装饰函数,比如如下代码:
go
package main
import (
"fmt"
)
//闭包1:静态变量
func gongzi_calc(base int) func(int) int {
base_gongzi := base
return func(a int) int {
return base_gongzi + a
}
}
//闭包2:装饰函数
// 闭包相当于函数试编程,函数构造器
// 有对象的特征,有静态变量
func eat_fan(preDo func()) func(int) {
preFunc := preDo
return func(a int) {
preFunc()
fmt.Printf("i eat a %d rice!\n", a)
}
}
func shuaya() {
fmt.Println("shuaya ing")
}
func xilian() {
fmt.Println("xilian ing")
}
func main() {
hr_gongzi_calc := gongzi_calc(3500)
engine_gongzi_calc := gongzi_calc(5000)
liming_gongzi := hr_gongzi_calc(1500)
fmt.Println("liming_gongzi:", liming_gongzi)
zhangsan_gongzi := hr_gongzi_calc(3000)
fmt.Println("zhangsan_gongzi:", zhangsan_gongzi)
wangwu_gongzi := engine_gongzi_calc(2300)
fmt.Println("wangwu_gongzi:", wangwu_gongzi)
xtt_eat_fun := eat_fan(shuaya)
zfj_eat_fun := eat_fan(xilian)
xtt_eat_fun(10)
zfj_eat_fun(2)
return
}
AI写代码go
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
3.2.5 元编程
元编程是编写能够操作自身或其他程序结构的程序 ,比如动态处理结构体字段、生成数据库语句、构建路由表、序列化等。Go 虽然是静态语言,但通过 reflect 包可实现一定程度的元编程能力。
- 常见元编程场景
| 场景 | 示例 |
|---|---|
| 自动绑定结构体字段 | 表单/JSON 转结构体 |
| 结构体转 Map | 通用数据库 ORM |
| 动态方法调用 | 插件、RPC |
| 自动生成 SQL 语句 | GORM、xorm 等 ORM 实现基础 |
| 参数注解校验 | 类似 Java 中的注解 |
比如使用反射+interface构建通用函数:
go
func SetField(obj interface{}, fieldName string, value interface{}) error {
v := reflect.ValueOf(obj).Elem()
f := v.FieldByName(fieldName)
if !f.IsValid() || !f.CanSet() {
return fmt.Errorf("field not found or not settable")
}
val := reflect.ValueOf(value)
if f.Type() != val.Type() {
return fmt.Errorf("type mismatch")
}
f.Set(val)
return nil
}
AI写代码go
运行