最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go?的入口函數和包初始化的使用

 更新時間:2022年05月10日 08:31:41   作者:小菠蘿測試筆記  
本文主要介紹了Go?的入口函數和包初始化的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

包 package

  • Go 包是 Go 語言的基本組成單元,一個 Go 程序就是一組包的集合,所有 Go 代碼都位于包中
  • Go 源碼可以導入其他 Go 包,并使用其中的導出語法元素,包括類型、變量、函數、方法等,而且 main 函數是整個 Go 應用的入口函數
  • Go 語言提供了很多內置包,如 fmt、os、io 等
  • 任何源代碼文件必須屬于某個包,同時源碼文件的第一行有效代碼必須是 package 包名語句,通過該語句聲明源碼文件所在的包

main.main 函數:Go 應用的入口函數

  • Go 語言中有一個特殊的函數:main 包中的 main 函數,也就是 main.main,它是所有 Go 可執(zhí)行程序的用戶層執(zhí)行邏輯的入口函數
  • Go 程序在用戶層面的執(zhí)行邏輯,會在這個函數內按照它的調用順序展開

package main

  • 整個 Go 可執(zhí)行程序中僅允許存在一個名為 main 的包
  • package main想要引用別的包的代碼,必須同樣以包的方式進行引用
package main

func main() {    
    // 用戶層執(zhí)行邏輯
    ... ...
}

Go 語言要求:可執(zhí)行程序的 main 包必須定義 main 函數,否則 Go 編譯器會報錯

注意

main 包是不可以像標準庫 fmt 包那樣被導入(Import)的

其他包也可以擁有 main 函數或方法

按照 Go 的可見性規(guī)則(小寫字母卡頭的標識符為非導出標識符),非 main包中自定義的 main 函數僅限于包內使用

package pkg1

import "fmt"

func Main () {
    main()
}

func main(){
    fmt.Println("main func for pkg1")
}

重點

  • 一個文件夾下的所有源碼文件只能屬于同一個包,不要求同名,但還是建議包名和所在目錄同名,這樣結構更清晰,包名中不能包含特殊符號
  • 給結構定義的方法必須放在同一個包內,可以是不同文件
  • 包名為 main 的包為應用程序的入口包,編譯不包含 main 包的源碼文件時不會得到可執(zhí)行文件。
  • 一個文件夾下的所有源碼文件只能屬于同一個包,屬于同一個包的源碼文件不能放在多個文件夾下

引子

不過對于 main 包的main 函數來說,還需要明確一點,就是它雖然是用戶層邏輯的入口函數,但它卻不一定是用戶層第一個被執(zhí)行的函數。這是為什么呢?這跟 Go 語言的另一個函數 init 有關

init 函數:Go 包的初始化函數

和 main.main 函數一樣,init 函數也是一個無參數無返回值的函數

func init() {
	// 包初始化邏輯
	... ...
}
  • Go 程序會在這個包初始化的時候,自動調用它的 init 函數,所以 init 函數的執(zhí)行會發(fā)生在 main 函數之前
  • 在 Go 程序中不能手工顯式地調用 init,否則會收到編譯錯誤

和 main 函數不一樣

  • init 函數在一個包中可以有多個,每個 Go 源文件都可以定義多個 init 函數

init 函數的執(zhí)行順序

  • 在初始化 Go 包時,Go 會按照一定的順序,逐一、順序地調用這個包的 init 函數
  • 一般來說,先傳遞給 Go 編譯器的源文件中的 init 函數,會先被執(zhí)行;而同一個源文件中的多個 init 函數,會按聲明順序依次執(zhí)行

Go 包的初始化次序

  • 從程序邏輯結構角度來看,Go 包是程序邏輯封裝的基本單元
  • 每個包都可以理解為是一個“自治”的、封裝良好的、對外部暴露有限接口的基本單元
  • 一個 Go 程序就是由一組包組成的,程序的初始化就是這些包的初始化
  • 每個 Go 包還會有自己的依賴包、常量、變量、init 函數(其中 main 包有 main 函數)等

三步走

  • 依賴包按“深度優(yōu)先”的次序進行初始化
  • 每個包內按以“常量 -> 變量 -> init 函數”的順序進行初始化
  • 包內的多個 init 函數按出現次序進行自動調用

init 函數的特點

  • 如上圖所示,執(zhí)行順位排在包內其他語法元素(常量、變量)的后面
  • 每個 init 函數在整個 Go 程序生命周期內僅會被執(zhí)行一次
  • init 函數是順序執(zhí)行的,只有當一個 init 函數執(zhí)行完畢后,才會去執(zhí)行下一個 init 函數

init 函數的用途

重置包級變量值

init 函數就好比 Go 包真正投入使用之前唯一的“質檢員”,負責對包內部以及暴露到外部的包級數據(主要是包級變量)的初始狀態(tài)進行檢查

實現對包級變量的復雜初始化

有些包級變量需要一個比較復雜的初始化過程,有些時候,使用它的類型零值或通過簡單初始化表達式不能滿足業(yè)務邏輯要求,而 init 函數則非常適合完成此項工作,標準庫 http 包中就有這樣一個典型示例

package main

import (
	"os"
	"strings"
)

var (
	http2VerboseLogs    bool // 初始化默認值 false
	http2logFrameWrites bool
	http2logFrameReads  bool
	http2inTests        bool
)

func init() {
	e := os.Getenv("GODEBUG")
	if strings.Contains(e, "http2debug=1") {
		http2VerboseLogs = true // 在 init 中對 http2VerboseLogs 的值進行重置
	}
	if strings.Contains(e, "http2debug=2") {
		http2logFrameWrites = true
		http2logFrameReads = true
		http2inTests = true
	}
}

http 包在init 函數中,就根據環(huán)境變量 GODEBUG 的值,對這些包級開關變量進行了復雜的初始化,從而保證了這些開關變量在 http 包完成初始化后,可以處于合理狀態(tài)

在 init 函數中實現“注冊模式”

來看一段使用 lib/pq 包訪問 PostgreSQL 數據庫的代碼 ??

package main

import (
	"database/sql"
	"log"

	_ "github.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=ver)
	if err != nil {
		log.Fatal(err)
	}
	age := 21
	rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
}
復制代碼

這里是以空導入_的方式導入 lib/pq 包的,main 函數中沒有使用 pq 包的任何變量、函數或方法,這樣就實現了對 PostgreSQL數據庫的訪問

實際原因

在 pq 包的 conn.go 源碼文件中的 init 函數

func init() {
	sql.Register("postgres", &Driver{})
}
  • 利用了空導入的特性,將 lib/pq 包作為 main 包的依賴包,在包初始化時,會先執(zhí)行 lib/pq 包里面的 init 函數
  • pq 包的 init 函數將自己實現的 sql 驅動注冊到了 sql 包中
  • 這樣在實際應用代碼中,Open 數據庫時,傳入驅動名字(這里是 postgres),就能得到數據庫實例,然后對數據庫進行操作,實際上是因為調用了 pq 包中相應的驅動實現的

好處:這種通過在 init 函數中注冊自己的實現的模式,就有效降低了 Go 包對外的直接暴露,尤其是包級變量的暴露,從而避免了外部通過包級變量對包狀態(tài)的改動

工廠設計模式

從標準庫 database/sql 包的角度來看,這種“注冊模式”實質是一種工廠設計模式的實現,sql.Open 函數就是這個模式中的工廠方法,它根據外部傳入的驅動名稱“生產”出不同類別的數據庫實例句柄

通過注冊模式實現獲取各種格式圖片的寬、高

Go 源碼

package main

import (
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"
)

func main() {
	// 支持 png、jpeg、gif
	width, height, err := imageSize(os.Args[1])
	if err != nil {
		fmt.Println("get image size error:", err)
		return
	}
	fmt.Printf("image size: [%d,%d]\n", width, height)
}

func imageSize(imageFile string) (int, int, error) {
    // 打開圖片文件
	f, _ := os.Open(imageFile)
	defer f.Close()
	
    // 對文件進行解碼,得到圖片實例
	img, _, err := image.Decode(f)
	if err != nil {
		return 0, 0, err
	}
	
    // 返回圖片區(qū)域
	b := img.Bounds()
	return b.Max.X, b.Max.Y, nil
}
  • 上面的源碼支持 png、jpeg、gif 三種格式的圖片
  • 但并不需要手動支持圖片格式
  • 是因為 image/png、image/jpeg 和 image/gif 包都在各自的 init 函數中,將自己“注冊”到 image 的支持格式列表中了
// $GOROOT/src/image/png/reader.go
func init() {
	image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}

// $GOROOT/src/image/jpeg/reader.go
func init() {
	image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig)
}

// $GOROOT/src/image/gif/reader.go
func init() {
	image.RegisterFormat("gif", "GIF8?a", Decode, DecodeConfig)
}

到此這篇關于Go 的入口函數和包初始化的使用的文章就介紹到這了,更多相關Go 入口函數和包初始化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 實時通信的服務器推送機制 EventSource(SSE) 簡介附go實現示例代碼

    實時通信的服務器推送機制 EventSource(SSE) 簡介附go實現示例代碼

    EventSource是一種非常有用的 API,適用于許多實時應用場景,它提供了一種簡單而可靠的方式來建立服務器推送連接,并實現實時更新和通知,這篇文章主要介紹了實時通信的服務器推送機制 EventSource(SSE)簡介附go實現示例,需要的朋友可以參考下
    2024-03-03
  • Golang中的四個括號示例詳解

    Golang中的四個括號示例詳解

    這篇文章主要介紹了Golang中的四個括號,本文通過實例代碼給大家介紹的非常詳細,通過實例代碼補充介紹了有效的括號golang實現,需要的朋友可以參考下
    2024-03-03
  • go?mod詳細使用教程

    go?mod詳細使用教程

    go mod是go的一個模塊管理工具,用來代替?zhèn)鹘y(tǒng)的GOPATH方案,下面這篇文章主要給大家介紹了關于go?mod詳細使用的相關資料,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • Golang操作命令行的幾種方式總結

    Golang操作命令行的幾種方式總結

    這篇文章主要介紹了Golang操作命令行的幾種方式總結,文章通過圍主題思想展開詳細的內容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • golang常用庫之配置文件解析庫-viper使用詳解

    golang常用庫之配置文件解析庫-viper使用詳解

    viper 配置管理解析庫,是由大神 Steve Francia 開發(fā),他在google領導著 golang 的產品開發(fā),他也是 gohugo.io 的創(chuàng)始人之一,命令行解析庫 cobra 開發(fā)者,這篇文章主要介紹了golang常用庫之配置文件解析庫-viper使用詳解,需要的朋友可以參考下
    2020-10-10
  • 詳解Golang 推薦的命名規(guī)范

    詳解Golang 推薦的命名規(guī)范

    這篇文章主要介紹了詳解Golang 推薦的命名規(guī)范,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • 詳解Golang中gcache模塊的基本使用

    詳解Golang中gcache模塊的基本使用

    這篇文章主要通過結合商業(yè)項目的使用場景,為大家介紹了gcache的基本使用、緩存控制以及淘汰策略。使用gcache做緩存處理,簡單方便易上手
    2022-11-11
  • 詳解Go?語言如何通過測試保證質量

    詳解Go?語言如何通過測試保證質量

    這篇文章主要為大家介紹了Go?語言如何通過測試保證質量詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Golang使用zlib壓縮和解壓縮字符串

    Golang使用zlib壓縮和解壓縮字符串

    本文給大家分享的是Golang使用zlib壓縮和解壓縮字符串的方法和示例,有需要的小伙伴可以參考下
    2017-02-02
  • Golang中接收者方法語法糖的使用方法詳解

    Golang中接收者方法語法糖的使用方法詳解

    這篇文章主要為大家詳細介紹了Golang中接收者方法語法糖的使用方法,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的可以了解一下
    2023-05-05

最新評論

保定市| 达孜县| 招远市| 镇巴县| 林口县| 孝昌县| 称多县| 青州市| 于都县| 兴海县| 那曲县| 哈密市| 汨罗市| 汉川市| 宣恩县| 临漳县| 股票| 前郭尔| 历史| 三穗县| 江西省| 渭南市| 长葛市| 白水县| 九台市| 阿合奇县| 德庆县| 吉安县| 东兴市| 长武县| 鸡西市| 嘉黎县| 泸定县| 从化市| 茶陵县| 英德市| 惠州市| 济源市| 马鞍山市| 和田市| 永兴县|