Go 语言入门:十问十答
这是「Golang学习」模块的第一篇。用问答的方式记录我和 AI 学习 Go 的过程——有踩坑记录、有顿悟时刻、也有经验总结。 Q1:Go 到底是什么?为什么又要学一门语言?A: Go(Golang)是 Google 在 2009 年发布的开源编程语言。核心卖点就三个: 特性 说明 编译快 大项目秒级编译,不像 C++ 那样喝杯咖啡回来还在编 并发原生 go 关键字 + channel,协程比线程轻量几个数量级 部署简单 编译出一个二进制文件,扔上去就能跑,无运行时依赖 你已经有 Java/Python/Go 的经验,Go 最适合的场景是:网络服务、微后端、CLI 工具、云基础设施。你的 slg-go 项目就是典型用例。 Q2:Hello World 长什么样?1234567package mainimport "fmt"func main() { fmt.Println("Hello, Izzy 👋")} 几点说明: package main — 每个 Go...
Go 面向对象(上):结构体与方法
上一篇聊了 Go 基础语法,这篇进入面向对象。Go 没有 class,但它的结构体+方法组合能实现几乎所有 OOP 能力——只是思维方式要转弯。 Q1:Go 的结构体和 Java/C# 的 class 有什么区别?A: 核心区别就一张表: 特性 Java Class Go Struct 定义 class Person { } type Person struct { } 继承 extends / implements 没有继承,用组合替代 构造函数 有构造方法 没有,用工厂函数模拟 this 指针 隐式传递 接收者显式声明 可见性 public / private 首字母大写 = 导出,小写 = 包内私有 12345678910// 定义结构体type Person struct { Name string // 导出字段(首字母大写) age int // 未导出字段(仅同包可访问)}// 工厂函数(替代构造函数)func NewPerson(name...
Go 语言进阶:从 Java 视角看 Go 的"反直觉"设计
第一篇聊了基础语法,第二篇从代码审查里踩了 40+ 个坑。这一篇换个角度——从你熟悉的 Java 出发,看 Go 那些让人第一反应”这不对吧”的设计。这些内容来自我和 AI 在开发 slg-go 项目时的真实对话。 背景:为什么总想用 Java 的思维写 Go?slg-go 是一个目标百万在线的 SLG 游戏服务器,Go 写的。我之前的主力语言是 Java,上手 Go 的前两周几乎每天都在经历这种内心戏: “这个方法怎么没有 try-catch?”“map 为什么不是线程安全的?”“for {} 这不是死循环吗?CPU 不就炸了?” 后来发现,这些”反直觉”恰恰是 Go 的设计哲学。这篇整理了我们讨论中最有价值的 7 个问题。 Q1:Go 和 Java 到底差在哪?不只是语法不同核心区别不在语法,在工程哲学。 维度 Java Go 错误处理 try-catch-throw 异常链 if err != nil 显式返回 并发 Thread + synchronized / Lock goroutine + channel + select ...
Go 面向对象(中):接口与鸭子类型
Go 的接口是隐式的——你不需要声明”我实现了某个接口”。这叫鸭子类型:如果它走起路来像鸭子,叫起来像鸭子,那它就是鸭子。但这个设计也埋了一些坑。 Q1:Go 的接口和 Java 的 interface 有什么本质区别?A: 一张表说清楚: 特性 Java Interface Go Interface 实现方式 class Dog implements Animal 显式声明 隐式满足,无需声明 方法集 必须实现所有方法 同左,但编译器自动检测 值/指针语义 只有引用类型 值接收者 vs 指针接收者有区别(大坑!) 空接口 无等价物 interface{} 可存任意值 接口组合 extends 多继承 嵌入接口 123456789101112131415// 定义接口type Speaker interface { Speak() string}// 注意:Dog 没有写 "implements Speaker"!type Dog struct{ Name string ...
Go 面向对象(下):组合优于继承
Go 的设计哲学:Composition over Inheritance(组合优于继承)。没有 extends、没有 protected、没有多态层级——Go 用更少的语言特性达到了同样的目标。但思维方式需要转弯。 Q1:Go 真的不能”继承”吗?那代码怎么复用?A: Go 用 组合(Composition)+ 嵌入(Embedding) 替代继承。先看三种组合模式: 模式一:匿名嵌入(最接近”继承”的感觉)12345678910111213141516type Engine struct { Power int // 马力}func (e *Engine) Start() { fmt.Printf("引擎启动!%d 马力\n", e.Power)}type Car struct { Engine // 匿名嵌入 —— Car 自动拥有 Engine 的字段和方法 Brand string}c := &Car{Engine: Engine...
Go 工程实践(上):Error 处理最佳实践
if err != nil 写到怀疑人生?这篇整理 Go 错误处理的正确姿势——从基础到进阶,让你不再害怕 error。 Q1:Go 的 error 到底是什么?为什么不用 try-catch?A: 1234// error 就是一个接口,就这:type error interface { Error() string} Go 选择 显式错误返回 而非异常机制的原因: try-catch (Java/Python) error return (Go) 控制流 可以跳过任意层 逐层显式处理 忽略错误 容易忘记 catch 编译器强迫你写 if err != nil 性能 异常有栈开销 零开销(就是个接口值) 可读性 正常逻辑和错误处理混在一起 分离清晰 代价就是代码里到处是 if err != nil。但这是特性不是 bug——它逼你面对每一个可能出错的地方。 Q2:三种创建 error 的方式分别什么时候用?A: 方式一:errors.New / fmt.Errorf — 最简单12345// 简...
Go Channel 底层原理:从源码到实战
前三篇聊了基础语法、代码审查陷阱和 Java→Go 的思维转变。但有一个话题一直没展开——Channel 到底是怎么工作的? 这篇从 hchan 数据结构出发,把 channel 的底层机制讲透,所有案例来自 slg-go 项目中的真实用法。 背景:为什么必须懂 Channel 底层?slg-go 战斗调度器里有这样一个关键设计: 12345678// internal/battle/scheduler/scheduler.go:126-143type Scheduler struct { requests chan *BattleRequest // 有缓冲 channel,任务队列 // ...}// 创建时指定缓冲大小s.requests = make(chan *BattleRequest, s.queueSize) // 默认 1000 123456789101112131415// internal/battle/scheduler/scheduler.go:337-356 — worker 从 channel 取任务f...
Go 工程实践(下):Testing 测试
Go 的测试哲学:测试代码也是一等公民。不需要复杂的框架,不需要额外的依赖——标准库 testing 包 + 几个约定就够了。这篇聊聊怎么写出”很 Go 风格”的测试。 Q1:Go 测试的基本规则是什么?A: 三条铁律: 规则 说明 文件名 _test.go 结尾(如 user_test.go) 函数名 TestXxx 开头(Xxx 首字母大写) 参数 只接受 *testing.T 123456789101112// user.gofunc Add(a, b int) int { return a + b}// user_test.gofunc TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2,3) = %d; want 5", result) }} 运行: 1234go test ./... # 运行所...
Go 并发进阶(上):常见并发模式
goroutine + channel 是 Go 的招牌,但会用和用好是两码事。这篇整理生产环境最实用的并发模式——每个都带完整可运行的代码。 Q1:Pipeline 模式是什么?A: 数据经过多个阶段处理,每阶段一个 goroutine,用 channel 连接: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546// 阶段1:生成数字func generate(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out}// 阶段2:平方func square(in <-chan int) <-chan i...
Go 并发进阶(中):sync 包深挖
sync 包是 Go 并发安全的基石。Mutex、RWMutex、WaitGroup、Once、Pool——每个都简单但都有坑。选对用对,性能差几倍;选错了,就是隐蔽的 data race。 Q1:Mutex vs RWMutex 怎么选?A: 1234567891011121314151617181920212223// Mutex —— 互斥锁(写锁)var mu sync.Mutexfunc (s *Store) Set(key string, val string) { mu.Lock() defer mu.Unlock() s.data[key] = val}// RWMutex —— 读写锁(读多写少场景)var rwmu sync.RWMutexfunc (s *Store) Get(key string) string { rwmu.RLock() // 读锁,多个 reader 可以同时持有 defer rwmu.RUnlock() return s.data[key]...