Go语言 Package 和 Import 学习笔记

Go语言 Package 和 Import 学习笔记

查看结论直接跳转到7,8,9即可

本博客主要记录:同一个目录下的go文件都得是一个package里面的,不允许同一个目录的的go文件属于第二个包。也就是一个目录对应一个package,而不同目录下的想要调用当前目录的go文件里面的代码必须使用import才可以进行调用

注:大小写控制的包内和包外是否可以调用,不管大小写,包内都随便调用,如果是大写,那么你在别的包使用import之后可以调用,如果是小写,那么在包外就无法被调用了

文章目录

  • [Go语言 Package 和 Import 学习笔记](#Go语言 Package 和 Import 学习笔记)
    • [一、核心问题:同一个 package 的文件能否互相调用?](#一、核心问题:同一个 package 的文件能否互相调用?)
      • [1.1 问题场景](#1.1 问题场景)
      • [1.2 答案](#1.2 答案)
      • [1.3 代码示例](#1.3 代码示例)
      • [1.4 运行方式](#1.4 运行方式)
    • [二、Go 的 Package 规则详解](#二、Go 的 Package 规则详解)
      • [2.1 核心规则](#2.1 核心规则)
      • [2.2 作用域示意图](#2.2 作用域示意图)
      • [2.3 注意事项](#2.3 注意事项)
        • [1. 同一目录不能有不同包名](#1. 同一目录不能有不同包名)
        • [2. 函数不能重复定义](#2. 函数不能重复定义)
    • [三、与 C++ 头文件机制的对比](#三、与 C++ 头文件机制的对比)
      • [3.1 整体对比](#3.1 整体对比)
      • [3.2 同一个项目内文件共享的对比](#3.2 同一个项目内文件共享的对比)
      • [3.3 引用其他目录/模块代码的对比](#3.3 引用其他目录/模块代码的对比)
      • [3.4 完整对比图](#3.4 完整对比图)
    • [四、Package 和 Import 的区别](#四、Package 和 Import 的区别)
      • [4.1 核心概念](#4.1 核心概念)
      • [4.2 package = 定义身份](#4.2 package = 定义身份)
      • [4.3 import = 引入别人](#4.3 import = 引入别人)
      • [4.4 核心区别总结](#4.4 核心区别总结)
    • 五、不同目录下的情况分析
      • [5.1 场景 A:三个文件在同一个目录,都是 `package main`](#5.1 场景 A:三个文件在同一个目录,都是 package main)
      • [5.2 场景 B:hello3.go 在另一个目录(错误示例)](#5.2 场景 B:hello3.go 在另一个目录(错误示例))
      • [5.3 场景 C:正确做法 - 抽取公共代码到独立包](#5.3 场景 C:正确做法 - 抽取公共代码到独立包)
      • [5.4 场景总结](#5.4 场景总结)
    • 六、常见错误示例
      • [6.1 错误:import 自己所在的包](#6.1 错误:import 自己所在的包)
      • [6.2 错误:import main 包](#6.2 错误:import main 包)
      • [6.3 错误:同目录不同包名](#6.3 错误:同目录不同包名)
    • [七、Go 的核心规则总结](#七、Go 的核心规则总结)
    • [八、与 C++ 的核心差异总结](#八、与 C++ 的核心差异总结)
    • 九、总结

一、核心问题:同一个 package 的文件能否互相调用?

1.1 问题场景

假设 hello1.gopackage mainhello2.go 也是 package main,那么在 hello1.go 中能否直接调用 hello2.go 中的函数?这两个文件是否属于同一个作用域?

1.2 答案

是的,可以!

如果两个文件满足以下条件:

  1. 声明相同的包名(如 package main
  2. 同一个目录

那么:

  • ✅ 可以直接调用对方的函数
  • ✅ 属于同一个包,共享同一个命名空间

1.3 代码示例

go 复制代码
// hello1.go
package main

import "fmt"

func main() {
    SayHello()  // ✅ 直接调用 hello2.go 中的函数
    fmt.Println("from hello1")
}
go 复制代码
// hello2.go
package main

import "fmt"

func SayHello() {
    fmt.Println("Hello from hello2!")
}

1.4 运行方式

bash 复制代码
# ❌ 错误:只运行一个文件,会报错 "undefined: SayHello"
go run hello1.go

# ✅ 正确方式1:运行所有相关文件
go run hello1.go hello2.go

# ✅ 正确方式2:运行整个目录
go run .

二、Go 的 Package 规则详解

2.1 核心规则

规则 说明
同目录 = 同包 同一个目录下的所有 .go 文件必须声明相同的包名
包内共享 同一个包内的所有代码共享命名空间,可以互相访问
大小写控制导出 首字母大写 = 可被其他包访问;首字母小写 = 仅包内访问

2.2 作用域示意图

go 复制代码
┌─────────────────────────────────────────┐
│            package main                 │
│  ┌─────────────┐  ┌─────────────┐       │
│  │ hello1.go   │  │ hello2.go   │       │
│  │             │  │             │       │
│  │ func main() │  │ func SayHello()     │
│  │   SayHello()│──│             │       │
│  └─────────────┘  └─────────────┘       │
│                                         │
│  ✅ 同一个作用域,可以互相访问              │
└─────────────────────────────────────────┘

2.3 注意事项

1. 同一目录不能有不同包名
go 复制代码
// ❌ 错误示例
// hello1.go
package main

// hello2.go  
package other  // 编译错误!同一目录下包名必须一致
2. 函数不能重复定义

其实就是在同一个作用域里面不能定义两个一样的函数

go 复制代码
// ❌ 两个文件都定义了同名函数
// hello1.go
func DoSomething() {}

// hello2.go
func DoSomething() {}  // 错误:重复定义

三、与 C++ 头文件机制的对比

3.1 整体对比

特性 C++ Go
代码组织 头文件(.h) + 源文件(.cpp) 包(package)
声明可见性 头文件声明 首字母大小写
引用方式 #include "xxx.h" import "xxx"
编译单位 单个 .cpp 文件 整个包
符号导出 所有声明都可见 大写开头才导出
链接 手动管理 .lib.o 自动处理

3.2 同一个项目内文件共享的对比

C++ 方式:需要头文件声明

cpp 复制代码
// ========== hello2.h ==========
#ifndef HELLO2_H
#define HELLO2_H

void SayHello();  // 声明

#endif

// ========== hello2.cpp ==========
#include "hello2.h"
#include <iostream>

void SayHello() {  // 实现
    std::cout << "Hello from hello2!" << std::endl;
}

// ========== hello1.cpp ==========
#include "hello2.h"  // 必须include头文件才能用

int main() {
    SayHello();  // 调用
    return 0;
}

Go 方式:同一个包内直接用

go 复制代码
// ========== hello2.go ==========
package main

import "fmt"

func SayHello() {  // 直接定义,无需头文件
    fmt.Println("Hello from hello2!")
}

// ========== hello1.go ==========
package main
// 不需要任何import!同包内直接用

func main() {
    SayHello()  // 直接调用
}

对比总结

C++ Go
同目录下的 .cpp 文件互不知道 同目录下的 .go 文件(同包)自动共享
必须用 #include 引入头文件 不需要,同包直接用
头文件 = 声明的桥梁 无需桥梁,编译器自动处理

3.3 引用其他目录/模块代码的对比

C++ 方式

cpp 复制代码
// ========== math_utils/math_utils.h ==========
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int Add(int a, int b);  // 声明
int Multiply(int a, int b);

#endif

// ========== math_utils/math_utils.cpp ==========
#include "math_utils.h"

int Add(int a, int b) { return a + b; }
int Multiply(int a, int b) { return a * b; }

// ========== main.cpp ==========
#include "math_utils/math_utils.h"  // 引入其他模块

int main() {
    int result = Add(1, 2);  // 使用
    return 0;
}

Go 方式

go 复制代码
// ========== math_utils/add.go ==========
package math_utils  // 包名

func Add(a, b int) int {  // 首字母大写 = 导出
    return a + b
}

func multiply(a, b int) int {  // 首字母小写 = 不导出
    return a * b
}

// ========== main.go ==========
package main

import "myproject/math_utils"  // 导入其他包

func main() {
    result := math_utils.Add(1, 2)     // ✅ 可以用
    // math_utils.multiply(1, 2)       // ❌ 编译错误,小写不可见
}

对比总结

C++ Go
#include "xxx.h" 引入声明 import "xxx" 引入整个包
头文件里声明的都能用 只有大写开头的能用
链接时需要 .lib.o 文件 自动链接,无需手动管理

3.4 完整对比图

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        C++ 头文件机制                            │
├─────────────────────────────────────────────────────────────────┤
│   hello2.h              hello2.cpp         hello1.cpp           │
│   ┌─────────┐          ┌─────────┐        ┌─────────┐          │
│   │ 声明    │◄──include│ 实现    │        │ #include│          │
│   │void Say│          │void Say │        │ "hello2"│          │
│   │Hello();│          │Hello(){ │        │         │          │
│   └─────────┘          │  ...    │        │ main(){ │          │
│        ▲               └─────────┘        │  SayHello()        │
│        │                                  └─────────┘          │
│        └────────── 必须通过头文件桥接 ──────────────┘          │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                        Go 包机制                                 │
├─────────────────────────────────────────────────────────────────┤
│   hello2.go                        hello1.go                    │
│   ┌─────────────────────┐         ┌─────────────────────┐      │
│   │ package main        │         │ package main        │      │
│   │                     │         │                     │      │
│   │ func SayHello() {   │─────────│ func main() {       │      │
│   │     fmt.Println()   │ 直接用! │     SayHello()      │      │
│   │ }                   │         │ }                   │      │
│   └─────────────────────┘         └─────────────────────┘      │
│        同一个 package = 自动共享,无需头文件                      │
└─────────────────────────────────────────────────────────────────┘

四、Package 和 Import 的区别

4.1 核心概念

Go 概念 作用 C++ 类比
package 定义"我是谁"(属于哪个包) namespace + 编译单元
import 引入"别人是谁"(使用其他包) #include + using

4.2 package = 定义身份

Go

go 复制代码
// hello.go
package myutils  // 我属于 myutils 包

func Add(a, b int) int {  // 大写 = 导出
    return a + b
}

C++ 类比

cpp 复制代码
// hello.cpp
namespace myutils {  // 我属于 myutils 命名空间
    int Add(int a, int b) {
        return a + b;
    }
}
Go C++
package myutils namespace myutils { ... }
定义这个文件属于哪个包 定义这些代码属于哪个命名空间
每个文件必须声明 可选,不是必须的

4.3 import = 引入别人

Go

go 复制代码
// main.go
package main

import "myproject/myutils"  // 引入 myutils 包

func main() {
    myutils.Add(1, 2)  // 通过包名调用
}

C++ 类比

cpp 复制代码
// main.cpp
#include "myutils/hello.h"  // 引入头文件

using namespace myutils;    // 使用命名空间

int main() {
    myutils::Add(1, 2);  // 通过命名空间调用
}
Go C++
import "xxx" #include "xxx.h" + 链接
引入整个包的符号 引入头文件的声明
自动链接 需要手动链接 .lib.o

4.4 核心区别总结

package = 定义身份(我是谁)

• 写在文件开头

• 声明这个文件属于哪个包

• 类似 C++ 的 namespace,但更强(目录必须对应)

import = 引入别人(我要用谁)

• 引入其他目录的包

• 类似 C++ 的 #include + using

• 但只能用大写开头的符号

类比:

package → "我是中国人"(定义身份)

import → "我要用美国货"(引入外部资源)


五、不同目录下的情况分析

5.1 场景 A:三个文件在同一个目录,都是 package main

复制代码
myproject/
├── hello1.go    (package main)
├── hello2.go    (package main)
└── hello3.go    (package main)
go 复制代码
// hello1.go
package main
func Func1() { println("hello1") }

// hello2.go
package main
func Func2() { println("hello2") }

// hello3.go
package main
// 不需要任何 import!直接用!
func main() {
    Func1()  // ✅ 直接调用
    Func2()  // ✅ 直接调用
}

结论 :同目录 + 同包名 = 直接用,不需要 import

5.2 场景 B:hello3.go 在另一个目录(错误示例)

复制代码
myproject/
├── main/
│   ├── hello1.go    (package main)
│   └── hello2.go    (package main)
└── other/
    └── hello3.go    (package other)
go 复制代码
// main/hello1.go
package main
func Func1() { println("hello1") }

// other/hello3.go
package other

// ❌ 不能 import "main"!main 包不能被导入!
// import "myproject/main"  // 编译错误!

func main() {  // ❌ 这个 main 函数也不会执行
    // 无法调用 Func1 和 Func2
}

问题

  1. main 包是程序入口不能被其他包 import
  2. 如果想让代码被其他包使用,必须放在非 main 包

5.3 场景 C:正确做法 - 抽取公共代码到独立包

复制代码
myproject/
├── myutils/
│   ├── hello1.go    (package myutils)
│   └── hello2.go    (package myutils)
└── main/
    └── hello3.go    (package main)
go 复制代码
// myutils/hello1.go
package myutils
func Func1() { println("hello1") }  // 大写开头 = 导出

// myutils/hello2.go
package myutils
func Func2() { println("hello2") }  // 大写开头 = 导出

// main/hello3.go
package main

import "myproject/myutils"  // ✅ 导入 myutils 包

func main() {
    myutils.Func1()  // ✅ 通过包名调用
    myutils.Func2()  // ✅ 通过包名调用
}

5.4 场景总结

场景 能否调用 原因
同目录 + 同包名 直接调用 同一个包,共享作用域
同目录 + 不同包名 编译错误 Go 要求同目录必须同包名
不同目录 + import "main" 编译错误 main 包不能被 import
不同目录 + 抽取到独立包 import 后调用 正确做法

六、常见错误示例

6.1 错误:import 自己所在的包

go 复制代码
// ❌ 错误示例
// hello3.go
package main
import "main"  // 编译错误!不能 import 自己所在的包

6.2 错误:import main 包

go 复制代码
// ❌ 错误示例
// other/hello3.go
package other
import "myproject/main"  // 编译错误!main 包不能被 import

6.3 错误:同目录不同包名

go 复制代码
// ❌ 错误示例
// hello1.go
package main

// hello2.go (同目录)
package other  // 编译错误!同目录必须同包名

七、Go 的核心规则总结

Go 的 package/import 规则:

  1. import 只能引入【其他包】,不能引入自己所在的包

  2. main 包是程序入口,不能被其他包 import

  3. 同目录 = 必须同包名 = 不需要 import = 直接用

  4. 不同目录 = 不同包 = 需要 import = 通过包名调用

  5. 大写开头 = 可被其他包使用(导出)

  6. 小写开头 = 只能包内使用(私有)


八、与 C++ 的核心差异总结

特性 C++ Go
同目录文件共享 需要头文件声明 同包自动共享
使用其他模块 #include "xxx.h" import "xxx"
符号可见性 头文件里写了就能用 大写=导出,小写=私有
编译链接 手动管理 .o .lib 自动处理
循环依赖 头文件可以循环 include 禁止循环 import
main 能否被引用 可以 不能被 import

九、总结

Go Package 机制速查表

• 每个文件必须声明 package

• 同目录必须同包名

• 类似 C++ 的 namespace

【import = 引入别人】

• 只能引入其他目录的包

• 不能 import 自己所在的包

• 不能 import main 包

• 类似 C++ 的 #include + using

【大小写控制可见性

• 大写开头 = 导出(可被其他包使用)

• 小写开头 = 私有(只能包内使用)

这一条不止针对函数,而是针对所有的标识符,比如函数,变量,结构体等等

【同包 = 同作用域】

• 同目录 + 同包名 = 直接用,不需要 import

• 不同目录 = 需要 import + 通过包名调用

同一个目录下的go文件都得是一个package里面的,不允许同一个目录的的go文件属于第二个包。

也就是一个目录对应一个package,而不同目录下的想要调用当前目录的go文件里面的代码必须使用import才可以进行调用