Skip to content

struct basics.go

文件信息

  • 📄 原文件:01_struct_basics.go
  • 🔤 语言:go

完整代码

go
package main

import (
	"encoding/json"
	"fmt"
)

// ============================================================
//                      结构体基础
// ============================================================
// 结构体是 Go 中最重要的复合类型
// 用于将多个相关的数据组织在一起
// Go 没有类(class),使用结构体 + 方法实现面向对象

func main() {
	fmt.Println("=== 结构体基础 ===")

	// ----------------------------------------------------------
	// 创建结构体实例
	// ----------------------------------------------------------

	// 方式1: 字段名赋值(推荐,顺序无关)
	p1 := Person{
		Name: "张三",
		Age:  25,
		City: "北京",
	}
	fmt.Printf("字段名赋值: %+v\n", p1)

	// 方式2: 按顺序赋值(不推荐,顺序敏感)
	p2 := Person{"李四", 30, "上海"}
	fmt.Printf("顺序赋值: %+v\n", p2)

	// 方式3: 零值初始化
	var p3 Person
	fmt.Printf("零值: %+v\n", p3) // 所有字段为零值

	// 方式4: 部分字段赋值(其余为零值)
	p4 := Person{Name: "王五"}
	fmt.Printf("部分赋值: %+v\n", p4)

	// 方式5: new 函数(返回指针)
	p5 := new(Person)
	p5.Name = "赵六"
	p5.Age = 35
	fmt.Printf("new 创建: %+v\n", *p5)

	// ----------------------------------------------------------
	// 访问和修改字段
	// ----------------------------------------------------------
	fmt.Println("\n=== 访问和修改字段 ===")

	fmt.Println("姓名:", p1.Name)
	fmt.Println("年龄:", p1.Age)

	p1.Age = 26 // 修改字段
	fmt.Println("修改后年龄:", p1.Age)

	// ----------------------------------------------------------
	// 结构体指针
	// ----------------------------------------------------------
	fmt.Println("\n=== 结构体指针 ===")

	pp := &Person{Name: "钱七", Age: 40, City: "广州"}

	// 【重要】Go 自动解引用,以下两种写法等价
	fmt.Println("指针访问:", pp.Name)   // 语法糖
	fmt.Println("显式解引用:", (*pp).Name) // 完整写法

	// 修改指针指向的值
	pp.Age = 41
	fmt.Printf("修改后: %+v\n", *pp)

	// ----------------------------------------------------------
	// 结构体比较
	// ----------------------------------------------------------
	fmt.Println("\n=== 结构体比较 ===")

	a := Person{Name: "Test", Age: 20, City: "Test"}
	b := Person{Name: "Test", Age: 20, City: "Test"}
	c := Person{Name: "Test", Age: 21, City: "Test"}

	fmt.Println("a == b:", a == b) // true
	fmt.Println("a == c:", a == c) // false

	// 【注意】包含切片、map、函数的结构体不能直接比较

	// ----------------------------------------------------------
	// 匿名结构体
	// ----------------------------------------------------------
	fmt.Println("\n=== 匿名结构体 ===")

	// 临时使用,无需定义类型
	point := struct {
		X, Y int
	}{10, 20}

	fmt.Printf("匿名结构体: %+v\n", point)

	// 常用于测试数据
	testCases := []struct {
		input    int
		expected int
	}{
		{1, 2},
		{2, 4},
		{3, 6},
	}
	fmt.Println("测试用例:", testCases)

	// ----------------------------------------------------------
	// 结构体嵌入(组合)
	// ----------------------------------------------------------
	fmt.Println("\n=== 结构体嵌入 ===")

	emp := Employee{
		Person:   Person{Name: "孙八", Age: 28, City: "深圳"},
		Title:    "工程师",
		Salary:   15000,
		Department: "技术部",
	}

	// 直接访问嵌入字段(字段提升)
	fmt.Println("姓名:", emp.Name)     // 等价于 emp.Person.Name
	fmt.Println("职位:", emp.Title)
	fmt.Printf("完整信息: %+v\n", emp)

	// ----------------------------------------------------------
	// 结构体标签(Tags)
	// ----------------------------------------------------------
	fmt.Println("\n=== 结构体标签 ===")

	user := User{
		ID:       1,
		Username: "johndoe",
		Email:    "[email protected]",
		Password: "secret123",
	}

	// JSON 序列化
	jsonData, _ := json.Marshal(user)
	fmt.Println("JSON:", string(jsonData))
	// 注意: password 字段被 json:"-" 排除

	// JSON 反序列化
	jsonStr := `{"id":2,"username":"janedoe","email":"[email protected]"}`
	var user2 User
	json.Unmarshal([]byte(jsonStr), &user2)
	fmt.Printf("反序列化: %+v\n", user2)

	// ----------------------------------------------------------
	// 结构体方法
	// ----------------------------------------------------------
	fmt.Println("\n=== 结构体方法 ===")

	rect := Rectangle{Width: 10, Height: 5}
	fmt.Println("面积:", rect.Area())
	fmt.Println("周长:", rect.Perimeter())
	fmt.Println("信息:", rect.String())

	// 指针方法
	rect.Scale(2)
	fmt.Println("缩放后:", rect.String())

	// ----------------------------------------------------------
	// 构造函数模式
	// ----------------------------------------------------------
	fmt.Println("\n=== 构造函数模式 ===")

	// Go 没有构造函数,使用工厂函数代替
	config := NewConfig("localhost", 8080)
	fmt.Printf("默认配置: %+v\n", config)

	config2 := NewConfigWithOptions("0.0.0.0", 80, true, 30)
	fmt.Printf("自定义配置: %+v\n", config2)

	main02()
	main03()
}

// ============================================================
//                      结构体定义
// ============================================================

// ----------------------------------------------------------
// 基本结构体
// ----------------------------------------------------------
// 语法: type 结构体名 struct { 字段名 类型 }
// 【命名规则】
// - 大写开头:可导出(public)
// - 小写开头:包内私有(private)

// Person 人员结构体
type Person struct {
	Name string // 可导出字段
	Age  int
	City string
}

// ----------------------------------------------------------
// 嵌入结构体(组合)
// ----------------------------------------------------------
// Go 推崇组合而非继承
// 嵌入的字段和方法会被提升到外层

// Employee 员工结构体
type Employee struct {
	Person           // 嵌入 Person(匿名字段)
	Title      string
	Salary     float64
	Department string
}

// ----------------------------------------------------------
// 带标签的结构体
// ----------------------------------------------------------
// 标签是字符串,用反引号包裹
// 常用于 JSON、数据库映射、验证等

// User 用户结构体(带标签)
type User struct {
	ID       int    `json:"id"`
	Username string `json:"username"`
	Email    string `json:"email"`
	Password string `json:"-"` // - 表示 JSON 序列化时忽略
}

// ----------------------------------------------------------
// 带方法的结构体
// ----------------------------------------------------------

// Rectangle 矩形
type Rectangle struct {
	Width  float64
	Height float64
}

// Area 计算面积(值接收者)
func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

// Perimeter 计算周长
func (r Rectangle) Perimeter() float64 {
	return 2 * (r.Width + r.Height)
}

// String 字符串表示
func (r Rectangle) String() string {
	return fmt.Sprintf("Rectangle{%.1f x %.1f}", r.Width, r.Height)
}

// Scale 缩放(指针接收者,修改原值)
func (r *Rectangle) Scale(factor float64) {
	r.Width *= factor
	r.Height *= factor
}

// ----------------------------------------------------------
// 构造函数(工厂函数)
// ----------------------------------------------------------
// 【惯例】函数名为 New + 类型名
// 【惯例】返回指针(大型结构体)或值(小型结构体)

// Config 配置结构体
type Config struct {
	Host    string
	Port    int
	Debug   bool
	Timeout int
}

// NewConfig 创建默认配置
func NewConfig(host string, port int) *Config {
	return &Config{
		Host:    host,
		Port:    port,
		Debug:   false,  // 默认值
		Timeout: 10,     // 默认值
	}
}

// NewConfigWithOptions 创建自定义配置
func NewConfigWithOptions(host string, port int, debug bool, timeout int) *Config {
	return &Config{
		Host:    host,
		Port:    port,
		Debug:   debug,
		Timeout: timeout,
	}
}

// ============================================================
//                      重要注意事项
// ============================================================
//
// 1. 【字段命名】
//    - 大写开头:可导出(其他包可访问)
//    - 小写开头:包内私有
//
// 2. 【零值】
//    结构体的零值是所有字段的零值
//    数值=0, 字符串="", 布尔=false, 指针/切片/map=nil
//
// 3. 【值 vs 指针】
//    - 小结构体(几个字段):传值
//    - 大结构体或需要修改:传指针
//    - 方法需要修改接收者:指针接收者
//
// 4. 【嵌入 vs 字段】
//    嵌入:Person(匿名,字段提升)
//    字段:person Person(命名,需要通过字段访问)
//
// 5. 【结构体比较】
//    - 所有字段可比较 → 结构体可比较
//    - 包含切片/map/函数 → 不可直接比较
//
// 6. 【标签使用】
//    - json: JSON 序列化
//    - db: 数据库映射
//    - validate: 验证规则
//    - 可通过 reflect 包读取

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布