导出字段与未导出字段
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 47 课,共 107 课。
在 Go 中,结构体层级的封装由字段名的大小写控制。这条简单的规则决定了能否从定义该结构体的包外部访问字段。
以大写字母开头的字段是已导出的:可从任何包访问。以小写字母开头的字段是未导出的:只能在同一个包内访问:
// 在 "user" 包中
type User struct {
Name string // 已导出,任何地方都可以访问
Email string // 已导出,任何地方都可以访问
password string // 未导出,只能在 "user" 包中访问
age int // 未导出,只能在 "user" 包中访问
}
当另一个包导入并使用这个结构体时,它只能与导出的字段交互:
// 在包 "main" 中
import "user"
func main() {
u := user.User{
Name: "Alice", // 可以正常运行
Email: "a@mail.com", // 可以正常运行
// password: "secret", // 编译错误:未知字段
}
fmt.Println(u.Name) // 可以正常运行
// fmt.Println(u.password) // 编译错误:无法引用未导出的字段
}
这种机制可以保护敏感数据和内部实现细节。无法从包外直接读取或修改 password 字段,因此其他代码必须使用你提供的方法。这是 Go 实现数据隐藏的方式,也是面向对象编程中封装的核心原则。
挑战
简单让我们构建一个银行账户系统,通过导出和未导出的字段来展示封装。你将创建一个包,在保护敏感财务数据的同时,公开安全的公共信息。
你将把代码分成两个文件:
account.go:在main包中创建一个BankAccount结构体,用来表示一个真实的银行账户,并妥善保护数据:HolderName(已导出):账户持有人的姓名,可以安全地公开显示AccountType(已导出):账户类型(例如,"Savings"、"Checking")balance(未导出):实际余额,应该受到保护pin(未导出):账户 PIN,是高度敏感的数据
NewBankAccount,接收持有人姓名、账户类型、初始余额和 PIN,然后返回一个指向新BankAccount的指针。再添加一个方法GetPublicInfo() string,返回以下格式:Account: [HolderName] ([AccountType])main.go:从输入中读取账户详情,使用构造函数创建一个BankAccount,并展示封装。使用方法打印公共信息,然后直接访问并分别打印每个已导出的字段。未导出的字段(balance和pin)受到保护。你只能通过构造函数设置这些字段。
输入内容如下:
- 第 1 行:账户持有人的姓名
- 第 2 行:账户类型
- 第 3 行:初始余额(浮点数)
- 第 4 行:PIN 码
例如,给定 Alice Johnson、Savings、5000.50 和 1234,你的输出应为:
Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: Savings注意,余额和 PIN 不会出现在输出中:它们安全地隐藏在结构体中,只能在包内访问。这就是封装的实际应用:敏感数据受到保护,同时公共信息仍可访问。
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取账户持有人姓名
holderName, _ := reader.ReadString('\n')
holderName = strings.TrimSpace(holderName)
// 读取账户类型
accountType, _ := reader.ReadString('\n')
accountType = strings.TrimSpace(accountType)
// 读取初始余额
balanceStr, _ := reader.ReadString('\n')
balanceStr = strings.TrimSpace(balanceStr)
balance, _ := strconv.ParseFloat(balanceStr, 64)
// 读取 PIN
pinStr, _ := reader.ReadString('\n')
pin := strings.TrimSpace(pinStr)
// TODO: 使用构造函数创建新的 BankAccount
// account := NewBankAccount(...)
// TODO: 使用 GetPublicInfo() 方法打印公开信息
// TODO: 直接访问导出字段,打印持有人姓名
// fmt.Println("Holder:", ...)
// TODO: 直接访问导出字段,打印账户类型
// fmt.Println("Type:", ...)
// 注意:balance 和 pin 未导出,不能在这里直接访问
// 它们被安全地封装在 struct 中。
_ = balance // 使用 balance 时,删除这一行。
_ = pin // 使用 pin 时,删除这一行。
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器