Menu
CoddyTech

导出字段与未导出字段

本课属于 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 实现数据隐藏的方式,也是面向对象编程中封装的核心原则。

challenge icon

挑战

简单

让我们构建一个银行账户系统,通过导出和未导出的字段来展示封装。你将创建一个包,在保护敏感财务数据的同时,公开安全的公共信息。

你将把代码分成两个文件:

  • 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 时,删除这一行。
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器