Go没有继承和类,仅通过结构体、方法、接口实现面向对象效果;结构体是内存布局描述,不带行为继承或访问控制;“继承”实为匿名字段嵌入与方法提升;多态依赖接口隐式实现;封装靠首字母大小写导出规则而非访问修饰符。

Go 没有继承,也没有类,谈“面向对象三大特性”时直接套用 Java/C++ 的理解会踩坑。它只提供结构体、方法、接口三样东西,所有所谓“封装、继承、多态”都是用这三样拼出来的效果,不是语言原生支持的机制。
结构体不是类,别按 class 去建模
很多人一写 type User struct 就默认这是个“类”,接着想“那父类呢?子类呢?构造函数呢?”。错——struct 就是内存布局描述,它不带行为继承、不带访问控制、不自动初始化。
真正起作用的是:你给谁定义了方法、方法接收者用值还是指针、字段名首字母大小写(决定是否导出)。
-
type Person struct { Name string; age int }中age小写 → 外部包无法读写,这不是“private”,只是不可导出 -
func (p Person) GetName() string是值接收者 → 方法内改p.Name不影响原变量 -
func (p *Person) SetName(n string)是指针接收者 → 才能修改原始实例
“继承”实际是匿名字段嵌入 + 方法提升
所谓“继承”,本质是结构体嵌入另一个结构体(必须是匿名字段),然后 Go 编译器自动把被嵌入类型的方法“提升”到外层结构体上。这不是运行时查找,是编译期静态展开。
比如:type Dog struct { Animal },Dog 能直接调 Speak(),是因为编译器把它等价于 dog.Animal.Speak(),而不是靠 vtable 或继承链。
立即学习“go语言免费学习笔记(深入)”;
- 嵌入非匿名字段(如
animal Animal)→ 不提升方法,必须写dog.animal.Speak() - 多个嵌入类型有同名方法(如两个匿名字段都有
Start())→ 编译报错,必须显式限定:dog.Engine.Start()或dog.Controller.Start() - 嵌入指针类型(如
*Engine)→ 提升行为一样,但空指针调用会 panic,需提前检查
多态靠接口,不是靠类型关系
Go 里没有“向上转型”或“父类引用指向子类实例”的概念。多态唯一途径是:让不同类型实现同一接口,然后用接口变量持有它们。
例如:type Speaker interface { Speak() },只要 Dog、Cat、Robot 都实现了 Speak() 方法,就能统一传给 func Say(s Speaker)。
- 接口是隐式实现的 —— 不需要
implements声明,写了方法就自动满足 - 空接口
interface{}可存任何值,但取出来要用类型断言或反射,否则无法调方法 - 接口变量本身包含两部分:动态类型(具体类型) + 动态值(数据),底层是 runtime.iface 结构
封装靠导出规则和组合,不是访问修饰符
Go 没有 public/private 关键字。封装靠首字母大小写:大写字段/方法可导出(跨包可见),小写不可导出(仅本包可用)。
但这只是编译期可见性控制,不是运行时保护 —— 同一包内依然能直接读写小写字段,也没法阻止反射绕过。
- 想真正隐藏内部状态?得靠组合 + 不导出字段 + 只暴露操作函数,比如用
type Config struct { timeout time.Duration }+func (c *Config) Timeout() time.Duration,不暴露timeout字段本身 - 别依赖小写字段“防误用”,它只防跨包误用;包内逻辑耦合过深时,小写字段反而会让重构更难
- 导出规则影响 JSON 序列化:
json.Marshal只序列化大写字母开头的字段


















