# 包package

1. Go使用包来组织源代码
2. 内置包源码位于`$GOROOT/src`下
3. 定义包：
   1. 包名一般是小写的，使用一个简短且有意义的名称
   2. 包名一般要和所在的目录同名，也可以不同，包名中不能包含`-` 等特殊符号
   3. 一个文件夹下的所有源码文件只能属于同一个包，同样属于同一个包的源码文件不能放在多个文件夹下
   4. 包的定义不包含路径，比如定义包：`package myp`，不会定义`package ppkg/myp`
   5. 包名为 main 的包为应用程序的入口包，编译不包含 main 包的源码文件时不会得到可执行文件
   6. 包一般使用域名作为目录名称，这样能保证包名的唯一性，比如 GitHub 项目的包一般会放到`GOPATH/src/github.com/userName/projectName` 目录下
4. 导入包：
   1. 导入包需要带上路径`import ppkg/myp`
   2. 包名称是从`$GOPATH/src/`下面进行计算的，使用`/`分割，`import ppkg/myp`的路径实际为`$GOPAT/src/ppkg/myp`

## 导入包

```go
// 单行导入
import "包 1. 入门 的路径"
import "包 2 的路径"

// 多行导入
import (
    "包 1. 入门 的路径"
    "包 2 的路径"
)

// 全路径导入
// 全路径是指GOROOT/src/或GOPATH/src/后面包的存放路径
import "lab/test"
import "database/sql/driver"
import "database/sql

// 相对路径导入
import "../a"
```

## 引用包

导入包后，需要对包内的成员进行引用，有以下几种引用方式

* 标准引用：

  ```go
  package main
  import "fmt" // 不使用包会报错
  func main() {
  	fmt.Println("hello") // 使用包名.成员名称
  }
  ```
* 自定义别名引用，当包名称有冲突时使用

  ```go
  package main
  import F "fmt" // 不使用包会报错
  func main() {
  	F.Println("hello") // 使用包别名.成员名称
  }
  ```
* 省略引用格式，将包内的内容合并到当前源文件中

  ```go
  package main
  import . "fmt" // 不使用包会报错
  func main() {
  	Println("hello") // 不需要任何前缀，直接使用
  }
  ```
* 匿名引用格式，如果只是希望执行包初始化的 init 函数，而不使用包内部的数据时，可以使用匿名引用格式，即使不使用包也不会报错

  ```go
  package main
  import (
  	_ "database/sql" // 即使不使用包，也不会报错
  	"fmt"
  )
  func main() {
  	fmt.Println("C语言中文网")
  }

  ```

## init函数

* 一个包可以有多个 init 函数，包加载时会执行全部的 init 函数，但并不能保证执行顺序，所以不建议在一个包中放入多个 init 函数，将需要初始化的逻辑放到一个 init 函数里面。
* 包不能出现环形引用的情况，比如包 a 引用了包 b，包 b 引用了包 c，如果包 c 又引用了包 a，则编译不能通过。
* 包的重复引用是允许的，比如包 a 引用了包 b 和包 c，包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d，这种情况是允许的，并且 Go 编译器保证包 d 的 init 函数只会执行一次。
* main包中的init函数将会先于main函数运行。

  ```go
  package main

  import "fmt"

  func init() {
  	fmt.Println("init")
  }
  func main() {
  	fmt.Println("Hello")
  }
  // 运行结果
  // init
  // Hello
  ```

## 包加载

![image-20210910155832935](/files/72ZHy5AQt0dVKHepRVeK)

Go语言包的初始化有如下特点：

* 包初始化程序从 main 函数引用的包开始，逐级查找包的引用，直到找到没有引用其他包的包，最终生成一个包引用的有向无环图。
* Go 编译器会将有向无环图转换为一棵树，然后从树的叶子节点开始逐层向上对包进行初始化。
* 单个包的初始化过程如上图所示，先初始化常量，然后是全局变量，最后执行包的 init 函数。

## 导出标识符

标识符包含变量、常量、方法等，只需要将其首字母大写即可导出：

```go
package mypkg

var myVar = 100
const MyConst = "hello"
type MyStruct struct {
}
```

如果结构体或者函数中的值也想导出，也将标识符首字母大写即可：

```go
type MyStruct struct {
    // 包外可以访问的字段
    ExportedField int
    // 仅限包内访问的字段
    privateField int
}

type MyInterface interface {
    // 包外可以访问的方法
    ExportedMethod()
    // 仅限包内访问的方法
    privateMethod()
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/golang/bao-package.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
