go中以小写字母或下划线开头的标识符为未导出(私有),仅在包内可见;大写字母开头则自动导出,供其他包通过packagename.identifier访问,这是由命名决定的静态规则,无private/public关键字。

Go 里小写字母开头的变量默认就是私有的
只要变量名以小写字母(或下划线)开头,它在包外就不可见——这是 Go 的导出规则,不是靠关键字、访问修饰符或作用域块控制的。没有 private、public 或 var privateName 这种写法。
常见错误现象:cannot refer to unexported name mypkg.myVar,说明你试图从其他包访问了一个小写开头的变量;反过来,如果别人能 import 你的包并直接用 mypkg.MyVar,那它一定是以大写字母开头的。
- 包级变量定义在
func外,且名字首字母小写 → 包内可用,包外不可见 - 首字母大写 → 自动导出,其他包可通过
import后使用PackageName.VarName - 下划线开头(如
_helper)也属于未导出,但通常只用于特殊场景(比如避免未使用警告),不推荐常规使用
为什么不能用 var 前加 private 或类似修饰符
Go 没有访问控制关键字。这不是遗漏,而是设计取舍:导出性由标识符拼写决定,编译器在构建时静态检查,不引入运行时语义或额外语法。加 private 不仅非法,还会报错 syntax error: unexpected private。
使用场景:当你写一个工具包(比如 jsonutil),内部需要缓存 sync.Pool 或配置解析结果,这些都不该暴露给调用方——直接写 var pool = sync.Pool{...} 就行,不用加注释说明“这是私有”,名字本身已说明一切。
立即学习“go语言免费学习笔记(深入)”;
- 所有包级声明(
var、const、type、func)都遵循同一套首字母规则 - 结构体字段也一样:小写字段(如
name string)在外部包中无法访问,哪怕结构体本身是导出的 - 方法名同样适用:只有
func (t T) Method()这样大写开头的方法才能被外部调用
容易踩的坑:大小写敏感 + IDE 自动补全误导
IDE(比如 Goland 或 VS Code + gopls)有时会把未导出变量列在自动补全里——但它只是“你在当前包里能看见”,不代表别的包也能用。一旦跨包引用,编译直接失败。
另一个典型问题:复制粘贴时改名不彻底。比如从 Config 改成 config,但忘了改初始化逻辑里的调用点,导致包内出现 undefined: config,而你还在奇怪“我明明定义了”。
- 检查是否导出,最可靠方式是看变量名第一个 Unicode 字母是否为大写(Go 使用 Unicode,不只是 ASCII A–Z)
- 别依赖 IDE 的“灰色提示”判断可见性;运行
go build才是最终裁判 - 如果变量只在单个文件里用,考虑放在函数内或用
init()初始化,避免污染包命名空间
小写字母变量也能被测试文件访问吗
能。Go 的测试文件(xxx_test.go)和源文件在同一个包里(除非显式声明为 xxx_test 包),所以可直接访问所有未导出变量。这是单元测试能验证内部状态的基础。
但要注意:如果测试文件声明了独立包名(比如 package mypkg_test),那它就成了外部包,此时小写变量就真不可见了——这种写法一般只用于黑盒测试或避免循环导入。
- 常规单元测试保持同包(即不写
package xxx_test),就能测var cache map[string]int这类内部状态 - 想测初始化逻辑?直接在
init()后加断言,或者把初始化抽成导出函数(如ResetForTest()),再用小写变量配合实现 - 不要为了测试而把变量改成大写——这等于暴露实现细节,违背封装意图










