Go语言标识符可见性唯一由首字母大小写决定:大写导出(对外可见),小写包内私有;方法、字段、类型、变量等均遵循此规则,且接收者类型与方法名必须同时导出才可跨包调用。
Go 语言中没有 public、private 关键字,模块可见性完全由标识符的首字母大小写决定——这是唯一规则,且不可绕过。
只有首字母为大写的常量、变量、函数、类型、字段、方法名才会被导出(即对外可见)。小写字母开头的标识符仅在定义它的包内可访问。
常见错误现象:在其他包中 import 后无法调用某个函数或访问某个字段,检查其首字母是否为小写。
MyFunc() ✅ 可导出;myFunc() ❌ 不可导出UserName ✅ 可导出;userName ❌ 包内私有type Config struct { Port int } → Port ✅ 可被外部访问;若写成 port int,则外部无法读写type Server struct { cfg *config } 中 cfg 小写,即使 config 类型本身导出了,该字段仍不可见init() 函数永远不导出,也不能被显式调用;它只在包初始化时自动执行。而包级变量是否导出,仍只看名字首字母。
立即学习“go语言免费学习笔记(深入)”;
容易忽略的点:即使一个变量是导出的,如果它被初始化为未导出类型的值(比如指向未导出结构体的指针),外部包也无法使用该值的字段或方法。
var DefaultClient = &httpClient{} → 若 httpClient 是小写类型,则外部无法访问其任何字段或方法,哪怕 DefaultClient 本身可导出var ErrInvalid = errors.New("invalid") ✅ 安全,因为 error 是导出接口,且 errors.New 返回的是导出类型实现type MyConfig = config 不会让 config 变得可导出方法能否被外部调用,不仅取决于方法名是否导出,还取决于其接收者类型是否导出。两者必须同时满足,方法才真正对外可见。
package data type user struct { // 小写,未导出 Name string } func (u *user) GetName() string { // 方法名导出,但接收者类型未导出 return u.Name } type User struct { // 大写,导出 Age int } func (u *User) GetAge() int { // ✅ 接收者类型 + 方法名均导出,可跨包调用 return u.Age }
上面例子中,GetName() 在其他包中不可见,即使你拿到了 *user 实例(你根本拿不到,因为 user 类型不可导出)。
最易被忽略的是组合嵌入:如果嵌入一个未导出字段(如 unexported unexportedType),即使该类型有导出方法,这些方法也不会“提升”为外部可见。可见性规则不穿透未导出字段边界。