親測 mac安裝Golang開發環境及快速入門

mac安裝Golang開發環境及快速入門

Rebecca.Yan

已於 2023-07-20 17:18:00 修改

閱讀量7.8k
 收藏 10

點贊數 4
分類專欄: Golang 文章標籤: macos golang
版權

雲原生技術專區
文章已被社區收錄
加入社區

Golang
專欄收錄該內容
2 篇文章1 訂閱
訂閱專欄
目錄

一、Mac brew 安裝go環境

1.1 安裝步驟

1.2 設置GOPATH 及環境變量

1.3 編寫第一個go程序

二、快速入門

2.1 快速入門需求

2.2 go學習(自用)

2.2.1 go基礎程序

2.2.2 變量聲明

2.2.3 常量和枚舉

2.2.4 函數與多種返回值

2.2.5 init函數與import導包

2.2.6 import匿名、別名導包

2.2.7 defer調用順序

2.2.8 數組的表示和動態數組 slice

2.2.9 map的聲明使用

三、debug

3.1 go: cannot run *_test.go files

3.2 深拷貝(copy)和淺拷貝(slice切片)

3.3 引用傳遞

一、Mac brew 安裝go環境
1.1 安裝步驟
1)終端輸入,也可以指定下載go版本

brew install go
2)查看golang當前版本

go version
3)查看當前golang環境 執行

go env
1.2 設置GOPATH 及環境變量
1)GOPATH 是工作目錄,工作空間目錄 go_project 就是一個目錄,其中包含三個子目錄:

src 目錄包含Go的源文件,它們被組織成包(每個目錄都對應一個包),
pkg 目錄包含包對象,
bin 目錄包含可執行命令。
如下圖:

​2) 終端輸入 vim .zshrc 添加:

GOPATH 後面加你自己的工作空間,這裏是我自己建立的go_project

export GOPATH="/Users/mima6ge0/Documents/yanhan_practice/go_project"
 
export PATH=$PATH:$GOPATH/bin  #等號前後不可有空格


執行 source ~/.zshrc 生效,使用go env查看是否修改成功

1.3 編寫第一個go程序
1)新建hellow.go文件

package main
 
import "fmt"
 
func main() {
    fmt.Println("hello, world")
}
2)進入文件所在目錄,執行go run hellow.go,成功,如下圖

二、快速入門
2.1 快速入門需求
具備1種後端編程語言開發經驗(C/C++/Java/Python/PHP)
具備基本的網絡編程能力和併發思想
瞭解計算機基本體系結構
瞭解Linux基礎知識
2.2 go學習(自用)
2.2.1 go基礎程序
package main //程序的包名
// main函數
//多個文件導入
import (
    "fmt"
    "time"
)
 
func main() { //函數的"{"必須和函數名在一行,換到下一行就是語法錯誤,強制代碼風格
    fmt.Println("hellow Go!")
    //go語言不需要";",可有可無
    time.Sleep(10 * time.Second)
}
2.2.2 變量聲明
package main
/*
變量的聲明方法
 */
import "fmt"
//全局變量,冒等方法不支持全局,其他的都可以
var A int = 100
func main() {
    //聲明變量默認值是0
    var a int
    fmt.Println("a = ", a)
    fmt.Printf("type of a = %T\n", a)
    //聲明變量,初始化
    var b int = 100
    fmt.Println("b = ", b)
    fmt.Printf("type of v = %T\n", b)
    //初始化可以省去變量類型,通過值去匹配變量的樹枝類型
    var c = 100
    fmt.Println("c = ", c)
    fmt.Printf("type of c = %T\n", c)
    //字符串
    var d string = "dasda"
    fmt.Println("d = ", d)
    fmt.Printf("type of d = %T\n", d)
    //最常用方法:冒等,省略var,直接自動匹配,":="初始化加賦值,函數內部使用
    e := 3213.432
    fmt.Println("e = ", e)
    fmt.Printf("type of e = %T\n", e)
 
    fmt.Println("A = ", A)
    fmt.Printf("type of A = %T\n", A)
 
    //聲明多個變量
    var x, y int = 100, 100
    fmt.Println("x = ", x, ", y = ", y)
    var m, n = 100, "88090"
    n = "kjuhku"
    fmt.Println("m = ", m, ", n = ", n)
    //多行多變量聲明
    var (
        xx int = 121
        zz bool = true
    )
    fmt.Println("xx = ", xx, ", zz = ", zz)
}

2.2.3 常量和枚舉
package main
 
import "fmt"
 
//const 可以定義枚舉類型
const (
    //可以在const裏面添加一個關鍵字 iota,每行的iota都會累加1,第一行的iota的默認值是0
    BEIJING = 10 * iota // iota = 0
    SHANGHAI        //iota = 10
    SHENZHEN        //iota = 20
)
 
const (
    a, b = iota + 1, iota + 2 //iota = 0, a = 1, b = 2
    c, d                      //iota = 1, c = 2, d = 3
    e, f                      //iota = 2, e = 3, f = 4
    g, h = iota * 2, iota * 3 //iota = 3, g = 6, h = 9
    i, k                      //iota = 4, i = 8, k = 12
)
 
func main() {
    //常量,只讀,不允許修改
    const a int = 100
    fmt.Println("a = ", a)
    fmt.Println("BEIJING = ", BEIJING)
    fmt.Println("SHANGHAI = ", SHANGHAI)
    fmt.Println("SHENZHEN = ", SHENZHEN)
 
    fmt.Println("a = ", a, "b = ", b)
    fmt.Println("c = ", c, "d = ", d)
    fmt.Println("e = ", e, "f = ", f)
    fmt.Println("g = ", g, "h = ", h)
    fmt.Println("i = ", i, "k = ", k)
    //iota只能在const裏面使用,var裏面不可以
    
 
}

2.2.4 函數與多種返回值
package main
 
import "fmt"
 
//返回一個值,匿名
func foo1(a string, b int) int {
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
    c := 100
    return c
}
 
//返回多個值,匿名
func foo2(a string, b int) (int, int) {
    fmt.Println("--------foo2----------")
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
    return 888, 9999
}
 
//返回多個返回值,有形參名稱
func foo3(a string, b int) (r1 int, r2 int) {
    fmt.Println("--------foo3----------")
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
 
    r1 = 1000
    r2 = 100000
    return 
}
 
func foo4(a string, b int) (r1, r2 int) {
    fmt.Println("--------foo4----------")
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
 
    //r1, r2屬於foo3的形參,初始化默認的值是0,作用域空間是整個foo4函數{}的整體
    fmt.Println("未賦值:r1 = ", r1)
    fmt.Println("未賦值:r2 = ", r2)
    r1 = 1000
    r2 = 100000
    return 
}
 
func main() {
    c := foo1("func1", 1)
    fmt.Println("c = ", c)
    ret1, ret2 := foo2("func2", 2)
    fmt.Println("ret1 = ", ret1)
    fmt.Println("ret2 = ", ret2)
 
    r1, r2 := foo3("func3", 3)
    fmt.Println("r1 = ", r1)
    fmt.Println("r2 = ", r2)
 
    r1, r2 = foo4("func4", 4)
    fmt.Println("r1 = ", r1)
    fmt.Println("r2 = ", r2)
 
}

2.2.5 init函數與import導包
文件目錄樹狀圖

lib1.go 代碼:

package lib1
 
import "fmt"
 
//當前lib1包提供的API
//首字母大寫的話代表當前接口對外開放,首字母小寫只能在該文件下使用
func Lib1Test () {
    fmt.Println("Lib1Test() ...")
}
 
func init () {
    fmt.Println("lib1 init ...")
}
lib2.go 代碼: 

package lib2
 
import "fmt"
 
//當前lib2包提供的API
func Lib2Test () {
    fmt.Println("Lib2Test() ...")
}
 
func init () {
    fmt.Println("lib2 init ...")
}
main.go代碼:

package main
 
//需要在GOPATH下
import (
    "GolangStudy/5_init/lib1"
    "GolangStudy/5_init/lib2"
)
 
 
func main () {
    lib1.Lib1Test()
    lib2.Lib2Test()
}
2.2.6 import匿名、別名導包
基於2.2.5的代碼

package main
 
//需要在GOPATH下,go語言語法比較嚴格,導入必須使用
import (
    // _ 匿名導包,導入但是不使用,不會報錯
    _ "GolangStudy/5_init/lib1"
 
    //mylib2是lib2的別名
    //mylib2 "GolangStudy/5_init/lib2"
 
    //可以不寫包名直接使用Lib2Test(),把方法導入當前main包裏面,不建議使用,如果有重名函數,會出現問題
    . "GolangStudy/5_init/lib2"
)
 
 
func main () {
    //lib1.Lib1Test()
 
    //mylib2.Lib2Test()
 
    Lib2Test() 
}

總結:

2.2.7 defer調用順序
package main
/*
defer的執行順序是在函數體全部執行完以後和結束之前,
多個defer語句是壓棧的,defer在return後面執行
*/
import "fmt"
 
func func1() {
    fmt.Println("A")
}
 
func func2() {
    fmt.Println("B")
}
 
func func3() {
    fmt.Println("C")
}
 
func main () {
    defer func1()
    defer func2()
    defer func3()
}

2.2.8 數組的表示和動態數組 slice
1)固定長度數組表示,遍歷等(不建議,不常用)

package main
import "fmt"
 
//不建議使用,可以使用動態數組
func printArray (array [4]int){
    //值拷貝
    fmt.Println("---------------輸出函數--------------")
    for i := 0; i < len(array); i++ {
        fmt.Println("array2_value = ", array[i])
    }
 
    //如果要對數組的值進行修改,下面這種傳參是改不了的
    // array[0] = 10000
}
func main () {
    // 固定長度數組
    var array1 [10]int
    for i := 0; i < len(array1); i++ {
        fmt.Println("array1_value = ", array1[i])
    }
    array2 := [10]int{1, 2, 4, 5}
    array3 := [4]int{1, 2, 4, 5}
    for index, value := range array2 {
        fmt.Println("array2_index = ", index, "array2_value = ", value)
    }
 
    printArray(array3)
 
    // 打印數組類型
    fmt.Printf("array1 type : %T\n", array1)
    fmt.Printf("array2 type : %T\n", array2)
    fmt.Printf("array3 type : %T\n", array3)
 
}

2)動態數組,切片,slice

package main
import "fmt"
 
func printArray(array []int) {
    // 引用拷貝,傳遞的是數組指針,所以array[0]會被修改爲100
    array[0] = 100
    // _ 表示匿名的變量,不被使用也不會發生問題
    for _, value := range array {
        fmt.Println("value = ", value)
    }
}
func main() {
    // 動態數組,切片,slice
    array := []int{1, 2, 3, 4}
    //查看array的類型
    fmt.Printf("array type : %T\n", array)
 
    printArray(array)
 
}

3)slice四種定義聲明方式

package main
 
import (
    "fmt"
 
)
func main() {
 
    // 聲明slice1是一個切片,並且初始化,長度是3,默認值是1, 2, 3
    // slice1 := []int{1, 2, 3}
 
    // 聲明slice1是一個切片,但是沒有分配空間,len = 0,value = []
    // var slice1 []int 
    // 可以通過make來開闢幾個空間,這時候len = 3,value = [0, 0, 0]
    // slice1 = make([]int, 3)
 
    // 聲明slice1是一個切片,通過make來開闢3空間,這時候len = 3,value = [0, 0, 0]
    // var slice1 []int = make([]int, 3)
 
    // 聲明slice1是一個切片,通過make來開闢3空間,這時候len = 3,value = [0, 0, 0],通過:=推測出他是一個切片
    slice1 := make([] int, 3)
 
    fmt.Printf("len = %d , value = %v\n", len(slice1), slice1)
 
    // 判斷一個切片是否爲空
    if slice1 == nil {
        fmt.Println("slice1 is null!")
    } else {
        fmt.Println("slice2 is not null!!")
    }
}

4)slice切片追加與截取

package main
 
import "fmt"
func main () {
    // 切片中len和cap是不同的
    var numbers = make([] int, 3, 5) // len = 3, cap容量 = 5
    fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
    //追加一個元素7
    numbers = append(numbers, 7)
    fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
    numbers = append(numbers, 8)
    // 當追加元素時,容量已滿,則自動擴充爲原來一開始定義的cap的二倍,擴充爲10
    numbers = append(numbers, 9)
    fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
 
    // 沒有定義容量的情況,但是在容量已滿的情況下追加元素,直接擴充爲len的二倍
    var numbers2 = make([] int, 3)
    fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers2), cap(numbers2), numbers2)
    numbers2 = append(numbers2, 4)
    fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers2), cap(numbers2), numbers2)
 
    // slice切片
    s := [] int{1, 2, 3, 4}
    s1 := s[0:2] //s1是s中的索引在[0, 2)區間的元素,和python類似
    fmt.Println("s1 = ", s1)
    s2 := s[1:] //s1是s中的索引在[1, len(s))區間的元素,和python類似
    fmt.Println("s2 = ", s2)
    //如若修改s1裏面的元素,s也會隨之改變,因爲這種切片相當於指針指向了s,所以改s1值,s和s1會一起發生改變(淺拷貝)
    s1[0] = 20000
    fmt.Println("s = ", s)
 
    // go提供了copy函數,切片的時候只將值複製過來,(深拷貝)
    s3 := make([] int, 3) //[0, 0, 0]
    // 將s2的值依次拷貝到s3中
    copy(s3, s2)
    fmt.Println("s3 = ", s3)
}

2.2.9 map的聲明使用
1)map的聲明

package main
 
import "fmt"
func main() {
    // 第一種聲明方式
    var map1 map[string]string
    if map1 == nil {
        fmt.Println("map1是一個空map!!")
    }
    // 使用前,需要通過make來給map分配數據空間
    map1 = make(map[string]string, 3)
    map1["one"] = "java"
    map1["two"] = "php"
    map1["three"] = "python"
    fmt.Println("map1 = ", map1)
 
    // 第二種聲明方式,直接:=
    map2 := make(map[int]string)
    map2[1] = "baidu"
    map2[2] = "tengxun"
    map2[3] = "ali"
    fmt.Println("map2 = ", map2)
 
    // 第三種聲明方式,一般帶有初始化的map用這種方式
    map3 := map[string]string{
        "王八": "綠豆",
        "傻子": "小丑",
    }
    fmt.Println("map3 = ", map3)
}

2)map的使用

package main
 
import (
    "fmt"
)
 
func printMap (city_map map[string]string) {
    // 遍歷
    for key, value := range city_map {
        fmt.Println("key = ", key, ", value = ", value)
    }
}
 
func changeMap (city_map map[string]string) {
    // city_map是一個引用傳遞,傳遞過來是地址,可以修改city_map
    city_map["UK"] = "london"
}
func main() {
    cityMap := make(map[string]string)
    cityMap["china"] = "beijing"
    cityMap["USA"] = "DC"
    
    
    printMap(cityMap)
 
    // 刪除
    delete(cityMap, "china")
    fmt.Println("-----------刪除後---------------")
    printMap(cityMap)
 
    // 修改
    cityMap["USA"] = "london"
    fmt.Println("-----------修改後---------------")
    printMap(cityMap)
 
    // 增加
    cityMap["china"] = "beijing"
    fmt.Println("-----------增加後---------------")
    printMap(cityMap)
 
    changeMap(cityMap)
    fmt.Println("-----------函數增加後---------------")
    printMap(cityMap)
 
 
 
}

2.2.10 面向對象

1)結構體的使用

package main
 
import "fmt"
 
// 聲明一種數據類型,myint是int的別名
type myint int
// 聲明結構體
type Book struct{
    title string
    auth string
}
 
func changeBook (book Book) {
    // 傳遞了一個book的副本
    book.title = "三體"
}
 
func changeBook2 (book *Book) {
    // 指針傳遞
    book.title = "生死疲勞"
}
 
func main () {
    var a myint = 232
    fmt.Println("a = ", a)
    fmt.Printf("type of a is %T\n", a)
 
    var book1 Book
    book1.title = "活着"
    book1.auth = "餘華"
    fmt.Printf("%v\n", book1)
 
    changeBook(book1)
    // 使用上面changeBook,修改值並沒有成功
    fmt.Printf("%v\n", book1)
 
    // 參數是地址,所以要加"&"
    changeBook2(&book1)
    fmt.Printf("%v\n", book1)
 
}

2)面向對象類的表示

A、封裝

package main
 
import "fmt"
 
// 類名、屬性名、方法名、首字母大寫代表其他包可以訪問,首字母小寫代表私有,只有本包內可以使用
type Hero struct {
    Name string
    Ad int
    Level int
}
 
// 如果不寫*Hero,這裏傳進去的一個副本,如果想在函數內對其進行修改,要用*Hero
func (this Hero) Show() {
    // 副本
    fmt.Println("Name : ", this.Name)
    fmt.Println("Ad : ", this.Ad)
    fmt.Println("Level : ", this.Level)
}
 
func (this Hero) GetName() string {
    return this.Name
}
 
func (this *Hero) SetName(name string) {
    // 這樣纔是當前對象
    this.Name = name
}
 
func main() {
    hero := Hero{ Name: "freezing", Ad: 33, Level: 1}
    hero.Show()
    hero.SetName("rebecca")
    hero.Show()
 
}

B、繼承

package main
 
import "fmt"
 
type Human struct {
    name string
    age int
}
 
func (this *Human) Walk() {
    fmt.Println("Human walk .....")
}
 
func (this *Human) Eat() {
    fmt.Println("Human eat .....")
}
 
 
type Superhero struct {
    // 繼承Human寫法
    Human
    level int
}
 
// 重定義(方法重寫)父類方法
func (this *Superhero) Eat() {
    fmt.Println("Superhero eat .....")
}
 
// 子類的新方法
func (this *Superhero) Fly() {
    fmt.Println("Superhero fly .....")
}
 
func main() {
    human := Human{"freezing", 20}
    human.Eat()
    human.Walk()
    superhero := Superhero{Human{"rebecca", 30}, 1}
    superhero.Walk() // 父類方法
    superhero.Eat()  // 子類方法
    superhero.Fly()  // 子類方法
    // 另外一種初始化對象的方法
    var s Superhero
    s.age = 24
    s.level  = 1
    s.name = "cookie wan"
}

2.2.10 json

1)json.Unmarshal反序列化,將json結構解析,如果解析過程中發生錯誤,json.Unmarshal 會返回一個非空的錯誤對象。

package main
 
import (
    "encoding/json"
    "fmt"
)
 
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}
 
func main() {
    jsonData := []byte(`{"name":"John", "age":30}`)
    var person Person
    // 用於將 JSON 格式的數據解析(反序列化)爲 Go 對象
    err := json.Unmarshal(jsonData, &person)
    if err != nil {
        fmt.Println("解析 JSON 失敗:", err)
        return
    }
    // John 30
    fmt.Println(person.Name, person.Age)
}

三、debug
3.1 go: cannot run *_test.go files
背景:將go文件命名成array_test.go報錯go: cannot run *_test.go files

原因:_test.go 是golang專用的測試文件

解決:改名

3.2 深拷貝(copy)和淺拷貝(slice切片)
https://blog.csdn.net/weixin_45440484/article/details/131740125

3.3 引用傳遞
引用傳遞是一種變量傳遞的方式,它不是直接傳遞變量的值,而是傳遞變量的內存地址(引用)。在引用傳遞中,被調用函數可以通過引用修改原始變量的值。
————————————————
版權聲明:本文爲CSDN博主「Rebecca.Yan」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_45440484/article/details/131692655

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章