国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

1688運(yùn)營(yíng)自學(xué)全套教程seo網(wǎng)站推廣工具

1688運(yùn)營(yíng)自學(xué)全套教程,seo網(wǎng)站推廣工具,網(wǎng)站備案手機(jī)號(hào)碼,北京新冠疫情最新情況初見(jiàn)golang語(yǔ)法 package mainimport "fmt"func main() {/* 簡(jiǎn)單的程序 萬(wàn)能的hello world */fmt.Println("Hello Go")} 第一行代碼package main定義了包名。你必須在源文件中非注釋的第一行指明這個(gè)文件屬于哪個(gè)包,如:package main…

初見(jiàn)golang語(yǔ)法

  package mainimport "fmt"func main() {/* 簡(jiǎn)單的程序 萬(wàn)能的hello world */fmt.Println("Hello Go")}
  • 第一行代碼package main定義了包名。你必須在源文件中非注釋的第一行指明這個(gè)文件屬于哪個(gè)包,如:package main。package main表示一個(gè)可獨(dú)立執(zhí)行的程序,每個(gè) Go 應(yīng)用程序都包含一個(gè)名為 main 的包
  • 下一行import "fmt"告訴 Go 編譯器這個(gè)程序需要使用 fmt 包(的函數(shù),或其他元素),fmt 包實(shí)現(xiàn)了格式化 IO(輸入/輸出)的函數(shù)

  • 下一行func main()是程序開(kāi)始執(zhí)行的函數(shù)。main 函數(shù)是每一個(gè)可執(zhí)行程序所必須包含的,一般來(lái)說(shuō)都是在啟動(dòng)后第一個(gè)執(zhí)行的函數(shù)(如果有 init() 函數(shù)則會(huì)先執(zhí)行該函數(shù))

注意:這里面go語(yǔ)言的語(yǔ)法,定義函數(shù)的時(shí)候,‘{’ 必須和函數(shù)名在同一行,不能另起一行

變量聲明

單獨(dú)變量聲明

第一種,指定變量類型,聲明后若不賦值,使用默認(rèn)值0

var a int
fmt.Printf(" = %d\n", a)

第二種,根據(jù)值自行進(jìn)行判斷變量類型

package mainimport ("fmt""reflect"
)func main() {var a = "abc"fmt.Println("a的類型是:", reflect.TypeOf(a))
}

第三種,省略var, 注意 :=左側(cè)的變量不應(yīng)該是已經(jīng)聲明過(guò)的,否則會(huì)導(dǎo)致編譯錯(cuò)誤

a := 10
b := "hello"var c int
c := 20 // 這里會(huì)報(bào)錯(cuò),因?yàn)?`c` 已經(jīng)聲明過(guò)了

總結(jié)

package mainimport "fmt"func main() {//第一種 使用默認(rèn)值var a intfmt.Printf("a = %d\n", a)//第二種var b int = 10fmt.Printf("b = %d\n", b)//第三種 省略后面的數(shù)據(jù)類型,自動(dòng)匹配類型var c = 20fmt.Printf("c = %d\n", c)//第四種 省略var關(guān)鍵字d := 3.14fmt.Printf("d = %f\n", d)
}

多變量聲明

package mainimport "fmt"var x, y int
var ( //這種分解的寫(xiě)法,一般用于聲明全局變量a intb bool
)var c, d int = 1, 2
var e, f = 123, "liudanbing"//這種不帶聲明格式的只能在函數(shù)體內(nèi)聲明
//g, h := 123, "需要在func函數(shù)體內(nèi)實(shí)現(xiàn)"func main() {g, h := 123, "需要在func函數(shù)體內(nèi)實(shí)現(xiàn)"fmt.Println(x, y, a, b, c, d, e, f, g, h)//不能對(duì)g變量再次做初始化聲明//g := 400_, value := 7, 5  //實(shí)際上7的賦值被廢棄,變量 _  不具備讀特性//fmt.Println(_) //_變量的是讀不出來(lái)的fmt.Println(value) //5
}

常量?

基本使用

常量是一個(gè)簡(jiǎn)單值的標(biāo)識(shí)符,在程序運(yùn)行時(shí),不會(huì)被修改的量。

常量中的數(shù)據(jù)類型只可以是布爾型、數(shù)字型(整數(shù)型、浮點(diǎn)型和復(fù)數(shù))和字符串型。

  • 常量的定義格式:?const identifier [type] = value
    • 顯示定義:const b string = "abc"
    • 隱式定義:const b = "abc"

例如:

package mainimport "fmt"func main() {const LENGTH int = 10const WIDTH int = 5   var area intconst a, b, c = 1, false, "str" //多重賦值area = LENGTH * WIDTHfmt.Printf("面積為 : %d\n", area)println(a, b, c)   
}//運(yùn)行結(jié)果
面積為 : 50
1 false str

常量聲明枚舉

在 Go 語(yǔ)言中沒(méi)有直接的枚舉類型,但是我們可以可以定義一組常量來(lái)表示枚舉值

const (Unknown = 0Female = 1Male = 2
)
// 數(shù)字 0、1 和 2 分別代表未知性別、女性和男性

常量可以用len(), cap(), unsafe.Sizeof()常量計(jì)算表達(dá)式的值。常量表達(dá)式中,函數(shù)必須是內(nèi)置函數(shù),否則編譯不過(guò):

package mainimport "unsafe"
const (a = "abc"b = len(a)c = unsafe.Sizeof(a)
)func main(){println(a, b, c)
}

iota標(biāo)識(shí)符

在我們定義一些常量的時(shí)候,可能需要給它們進(jìn)行賦值。在Go語(yǔ)言當(dāng)中,就可以簡(jiǎn)化這個(gè)賦值操作通過(guò)iota標(biāo)示符,如:

package mainimport "fmt"const (Sunday = iotaMondayTuesdayWednesdayThursdayFridaySaturday
)func main() {today := Mondayfmt.Println("Today is:", today)
}

iota不僅僅用于自增,還可以根據(jù)定義一些百搭是,來(lái)進(jìn)行賦值操作,如:

const (d = iota * 2ef
)func main() {fmt.Println(d) // 0fmt.Println(e) // 2fmt.Println(f) // 4
}

函數(shù)

函數(shù)返回多個(gè)值

Go的函數(shù)可以返回多個(gè)值,例如:

package mainimport "fmt"func swap(x, y string) (string, string) {return y, x
}
func main() {a, b := swap("a", "b")fmt.Println(a, " ", b)
}//執(zhí)行結(jié)果:b   a

init函數(shù)和import

init 函數(shù)可在package main中,可在其他package中,可在同一個(gè)package中出現(xiàn)多次,而main 函數(shù)只能在package main中

執(zhí)行順序

golang里面保留了兩個(gè)函數(shù):init函數(shù)(能應(yīng)用于所有的package)和main函數(shù)(只能應(yīng)用于package main)。這兩個(gè)函數(shù)在定義的時(shí)候不能有任何參數(shù)和返回值

雖然一個(gè)package里面可以寫(xiě)任意多個(gè)init函數(shù),但這無(wú)論是對(duì)于可讀性還是以后的可維護(hù)性來(lái)說(shuō),我們都強(qiáng)烈建議用戶在一個(gè)package中每個(gè)文件只寫(xiě)一個(gè)init函數(shù)。go程序會(huì)自動(dòng)調(diào)用init()和main(),所以你不需要在任何地方調(diào)用這兩個(gè)函數(shù)。每個(gè)package中的init函數(shù)都是可選的,但package main就必須包含一個(gè)main函數(shù)

程序的初始化和執(zhí)行都起始于main包。如果main包還導(dǎo)入了其它的包,那么就會(huì)在編譯時(shí)將它們依次導(dǎo)入。有時(shí)一個(gè)包會(huì)被多個(gè)包同時(shí)導(dǎo)入,那么它只會(huì)被導(dǎo)入一次(例如很多包可能都會(huì)用到fmt包,但它只會(huì)被導(dǎo)入一次,因?yàn)闆](méi)有必要導(dǎo)入多次)。當(dāng)一個(gè)包被導(dǎo)入時(shí),如果該包還導(dǎo)入了其它的包,那么會(huì)先將其它包導(dǎo)入進(jìn)來(lái),然后再對(duì)這些包中的包級(jí)常量和變量進(jìn)行初始化,接著執(zhí)行init函數(shù)(如果有的話),依次類推。等所有被導(dǎo)入的包都加載完畢了,就會(huì)開(kāi)始對(duì)main包中的包級(jí)常量和變量進(jìn)行初始化,然后執(zhí)行main包中的init函數(shù)(如果存在的話),最后執(zhí)行main函數(shù)。下圖詳細(xì)地解釋了整個(gè)執(zhí)行過(guò)程:

代碼示例:

package InitLib1import "fmt"func init() {fmt.Println("lib1")
}package InitLib2import "fmt"func init() {fmt.Println("lib2")
}package mainimport ("fmt"_ "GolangTraining/InitLib1"_ "GolangTraining/InitLib2"
)func init() {fmt.Println("libmain init")
}func main() {fmt.Println("libmian main")
}//執(zhí)行結(jié)果:
lib1
lib2
libmain init
libmian main

輸出的順序與我們上面圖給出的順序是一致的。那我們現(xiàn)在就改動(dòng)一個(gè)地方,Lib1包導(dǎo)入Lib2,main包不管:

package InitLib1import ("fmt"_ "GolangTraining/InitLib2"
)func init() {fmt.Println("lib1")
}//輸出結(jié)果:
lib2
lib1
libmain init
libmian main

main包以及Lib1包都導(dǎo)入了Lib2,但是只出現(xiàn)一次,并且最先輸出,說(shuō)明如果一個(gè)包會(huì)被多個(gè)包同時(shí)導(dǎo)入,那么它只會(huì)被導(dǎo)入一次,而先輸出lib2是因?yàn)閙ain包中導(dǎo)入Lib1時(shí),Lib1又導(dǎo)入了Lib2,會(huì)首先初始化Lib2包的東西

注意:如果需要將當(dāng)前的包函數(shù)提供給外部,需要將函數(shù)名首字母大寫(xiě)!!!否則會(huì)引用不了!!!

函數(shù)參數(shù)

函數(shù)的參數(shù)在傳遞的時(shí)候主要是分為兩種類型:值傳遞指針傳遞

值傳遞:值傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對(duì)參數(shù)進(jìn)行修改,將不會(huì)影響到實(shí)際參數(shù)

/* 定義相互交換值的函數(shù) */
func swap(x, y int) int {var temp inttemp = x /* 保存 x 的值 */x = y    /* 將 y 值賦給 x */y = temp /* 將 temp 值賦給 y*/return temp;
}

指針傳遞:指針傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址傳遞到函數(shù)中,那么在函數(shù)中對(duì)參數(shù)所進(jìn)行的修改,將影響到實(shí)際參數(shù)

/* 定義交換值函數(shù)*/
func swap(x *int, y *int) {var temp inttemp = *x    /* 保持 x 地址上的值 */*x = *y      /* 將 y 值賦給 x */*y = temp    /* 將 temp 值賦給 y */
}

defer

defer類似于我們Java中的finally,它會(huì)在我們程序執(zhí)行的最后執(zhí)行一些任務(wù)defer語(yǔ)句被用于預(yù)定對(duì)一個(gè)函數(shù)的調(diào)用??梢园堰@類被defer語(yǔ)句調(diào)用的函數(shù)稱為延遲函數(shù)。

defer作用:

  • 釋放占用的資源
  • 捕捉處理異常
  • 輸出日志

如果一個(gè)函數(shù)中有多個(gè)defer語(yǔ)句,它們會(huì)以LIFO(后進(jìn)先出)的順序執(zhí)行:

func Demo(){defer fmt.Println("1")defer fmt.Println("2")defer fmt.Println("3")defer fmt.Println("4")
}
func main() {Demo()
}//運(yùn)行結(jié)果:4 3 2 1

panic和recover

panic(宕機(jī)) 和 recover(恢復(fù))是Go語(yǔ)言的兩個(gè)內(nèi)置函數(shù),這兩個(gè)內(nèi)置函數(shù)用來(lái)處理Go的運(yùn)行時(shí)錯(cuò)誤(runtime errors)。panic 用來(lái)主動(dòng)拋出異常,recover用來(lái)捕獲panic拋出的異常

引發(fā)panic有兩種情況:一種是程序主動(dòng)調(diào)用panic()函數(shù),另一種是程序產(chǎn)生運(yùn)行時(shí)錯(cuò)誤,由運(yùn)行時(shí)檢測(cè)并拋出。例如:

示例1:程序主動(dòng)調(diào)用panic,觸發(fā)宕機(jī),讓程序崩潰

func funcA() {fmt.Println("func A")
}func funcB() {panic("panic in B")
}func funcC() {fmt.Println("func C")
}
func main() {funcA()funcB()funcC()
}
運(yùn)行結(jié)果:func A
panic: panic in Bgoroutine 1 [running]:
main.funcB(...)/home/wangxm/go_work/src/chapter05/demo.go:12
main.main()/home/wangxm/go_work/src/chapter05/demo.go:20 +0x96
exit status 2

當(dāng)在funcB中主動(dòng)調(diào)用了panic 函數(shù)后,程序發(fā)生宕機(jī)直接退出了,同時(shí)輸出了堆棧和goroutine相關(guān)信息,這讓我們可以看到錯(cuò)誤發(fā)生的位置。當(dāng)panic()觸發(fā)的宕機(jī)發(fā)生時(shí),panic后面的代碼將不會(huì)被執(zhí)行,但是在panic()函數(shù)前面的已經(jīng)執(zhí)行過(guò)的defer語(yǔ)句依然會(huì)在宕機(jī)發(fā)生時(shí)執(zhí)行defer中的延遲函數(shù)。

示例2:在宕機(jī)時(shí)觸發(fā)defer語(yǔ)句延遲函數(shù)的執(zhí)行:

func funcA() {fmt.Println("func A")
}func funcB() {panic("panic in B")
}func funcC() {fmt.Println("func C")
}func funcD() {fmt.Println("func D")
}func main() {defer funcA()defer funcC()fmt.Println("this is main")funcB()defer funcD()
}
運(yùn)行結(jié)果:this is main
func C
func A
panic: panic in Bgoroutine 1 [running]:
main.funcB(...)/home/wangxm/go_work/src/chapter05/demo.go:12
main.main()/home/wangxm/go_work/src/chapter05/demo.go:29 +0xca
exit status 2

從運(yùn)行結(jié)果可以看出,當(dāng)程序流程正常執(zhí)行到funcB()函數(shù)的panic語(yǔ)句時(shí),在panic()函數(shù)被執(zhí)行前,defer語(yǔ)句會(huì)優(yōu)先被執(zhí)行,defer語(yǔ)句的執(zhí)行順序是先進(jìn)后出,所以funcC()延遲函數(shù)先執(zhí)行,funcA()后執(zhí)行,當(dāng)所有已注冊(cè)的defer語(yǔ)句都執(zhí)行完畢,才會(huì)執(zhí)行panic()函數(shù),觸發(fā)宕機(jī),程序崩潰退出,因此程序流程執(zhí)行不到funcD()函數(shù)。

發(fā)生panic后,程序會(huì)從調(diào)用panic的函數(shù)位置或發(fā)生panic的地方立即返回,逐層向上執(zhí)行函數(shù)的defer語(yǔ)句,然后逐層打印函數(shù)調(diào)用堆棧信息,直到被recover捕獲或運(yùn)行到最外層函數(shù)而退出。如本例中,程序從funcB()函數(shù)返回到上層的main()函數(shù)中,然后執(zhí)行已注冊(cè)的defer語(yǔ)句的延遲函數(shù),最后從main函數(shù)中退出,并且打印了退出狀態(tài)碼的值為2

recover()函數(shù)用來(lái)捕獲或者說(shuō)是攔截panic的,阻止panic繼續(xù)向上層傳遞。無(wú)論是主動(dòng)調(diào)用panic()函數(shù)觸發(fā)的宕機(jī)還是程序在運(yùn)行過(guò)程中由Runtime層拋出的異常,都可以配合defer 和 recover 實(shí)現(xiàn)異常捕獲和恢復(fù),讓代碼在發(fā)生panic后能夠繼續(xù)執(zhí)行

Go語(yǔ)言沒(méi)有異常系統(tǒng),其使用panic觸發(fā)宕機(jī)類似于其他語(yǔ)言的拋出異常,而recover的宕機(jī)恢復(fù)機(jī)制就對(duì)應(yīng)try...catch機(jī)制

示例:使用recover捕獲panic異常,恢復(fù)程序的運(yùn)行:

func funcA() {fmt.Println("func A")
}func funcB() {defer func(){//捕獲panic,并恢復(fù)程序使其繼續(xù)運(yùn)行if err := recover(); err != nil {fmt.Println("recover in funcB")}}()panic("panic in B")  //主動(dòng)拋出異常
}func funcC() {fmt.Println("func C")
}func funcD() {fmt.Println("func D")
}func main() {defer funcA()defer funcC()fmt.Println("this is main")funcB()defer funcD()
}運(yùn)行結(jié)果:this is main
recover in funcB
func D
func C
func A

當(dāng)recover捕獲到panic時(shí),不會(huì)造成整個(gè)進(jìn)程的崩潰,它會(huì)從觸發(fā)panic的位置退出當(dāng)前函數(shù),然后繼續(xù)執(zhí)行后續(xù)代碼

IF判斷

在Go語(yǔ)言中,if語(yǔ)句用于條件判斷,它有以下幾種常見(jiàn)的用法和特點(diǎn):

基本用法

  • 語(yǔ)法結(jié)構(gòu)
    if condition {// 當(dāng)條件為真時(shí)執(zhí)行的代碼塊
    }
    
    其中condition是一個(gè)布爾表達(dá)式,如果condition的值為true,則執(zhí)行花括號(hào)內(nèi)的代碼塊。
  • 示例
    num := 10
    if num > 5 {fmt.Println("num大于5")
    }
    

帶有else子句

  • 語(yǔ)法結(jié)構(gòu)
    if condition {// 當(dāng)條件為真時(shí)執(zhí)行的代碼塊
    } else {// 當(dāng)條件為假時(shí)執(zhí)行的代碼塊
    }
    
  • 示例
    num := 3
    if num > 5 {fmt.Println("num大于5")
    } else {fmt.Println("num小于等于5")
    }
    

帶有else if子句

  • 語(yǔ)法結(jié)構(gòu)
    if condition1 {// 當(dāng)條件1為真時(shí)執(zhí)行的代碼塊
    } else if condition2 {// 當(dāng)條件2為真時(shí)執(zhí)行的代碼塊
    } else {// 當(dāng)條件1和條件2都為假時(shí)執(zhí)行的代碼塊
    }
    
  • 示例
    num := 7
    if num > 10 {fmt.Println("num大于10")
    } else if num > 5 {fmt.Println("num大于5且小于等于10")
    } else {fmt.Println("num小于等于5")
    }
    

if語(yǔ)句中聲明和初始化變量

  • 語(yǔ)法結(jié)構(gòu)
    可以在if語(yǔ)句的條件判斷部分同時(shí)聲明和初始化一個(gè)變量,這個(gè)變量的作用域僅限于if語(yǔ)句及其相關(guān)的elseelse if子句
    if var_declaration := expression; condition {// 當(dāng)條件為真時(shí)執(zhí)行的代碼塊,且可以使用var_declaration變量
    }
    
  • 示例
    if num := 8; num > 5 {fmt.Println("num大于5,num的值為:", num)
    }
    
    在這個(gè)例子中,numif語(yǔ)句中被聲明和初始化,并且只能在if相關(guān)的代碼塊中使用。如果numif語(yǔ)句外已經(jīng)存在,那么在if語(yǔ)句中使用這種方式聲明num會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)?code>if語(yǔ)句中這種方式的變量聲明會(huì)被視為一個(gè)新的局部變量聲明

循環(huán)

在Go語(yǔ)言中,有兩種主要的循環(huán)結(jié)構(gòu):for循環(huán)和range循環(huán)(range可以看作是一種特殊的基于for循環(huán)的便利形式,用于迭代容器類型的數(shù)據(jù))。

for循環(huán)

  1. 基本for循環(huán)

    • 語(yǔ)法結(jié)構(gòu)
      for initialization; condition; post {// 循環(huán)體
      }
      
      • initialization:循環(huán)開(kāi)始前的初始化操作,通常用于聲明和初始化循環(huán)變量。
      • condition:循環(huán)的條件判斷,只要該條件為true,循環(huán)就會(huì)繼續(xù)執(zhí)行。
      • post:每次循環(huán)結(jié)束后執(zhí)行的操作,通常用于更新循環(huán)變量。
    • 示例
      for i := 0; i < 5; i++ {fmt.Println(i)
      }
      
      這個(gè)循環(huán)會(huì)從i = 0開(kāi)始,只要i < 5就會(huì)執(zhí)行循環(huán)體,每次循環(huán)結(jié)束后i的值會(huì)增加1,最終會(huì)打印出04這幾個(gè)數(shù)字。

range循環(huán)

  1. 用于迭代數(shù)組和切片

    • 語(yǔ)法結(jié)構(gòu)
      for index, value := range arrayOrSlice {// 對(duì)索引和值進(jìn)行處理
      }
      
      其中index是數(shù)組或切片元素的索引,value是元素的值。對(duì)于每個(gè)元素,循環(huán)都會(huì)執(zhí)行一次。
    • 示例
      arr := []int{1, 2, 3, 4, 5}
      for index, value := range arr {fmt.Printf("索引為 %d 的元素值為 %d\n", index, value)
      }
      
      這個(gè)循環(huán)會(huì)遍歷切片arr,并打印出每個(gè)元素的索引和值。
  2. 用于迭代字符串

    • 字符串在Go語(yǔ)言中可以看作是一個(gè)字節(jié)數(shù)組,所以也可以用range循環(huán)來(lái)迭代。
    • 語(yǔ)法結(jié)構(gòu)
      for index, value := range stringValue {// 對(duì)索引和值進(jìn)行處理// 需要注意的是,這里的價(jià)值可能是一個(gè)字節(jié)(對(duì)于ASCII字符)或者是一個(gè)Unicode碼點(diǎn)的第一個(gè)字節(jié)(對(duì)于非ASCII字符)// 如果要獲取完整的Unicode碼點(diǎn),需要進(jìn)一步處理
      }
      
    • 示例
      str := "hello"
      for index, value := range str {fmt.Printf("索引為 %d 的字符值為 %c\n", index, rune(value))
      }
      
      這里使用rune函數(shù)將字節(jié)值轉(zhuǎn)換為Unicode碼點(diǎn)對(duì)應(yīng)的字符,以便正確打印出字符串中的字符。
  3. 用于迭代映射(map)

    • 語(yǔ)法結(jié)構(gòu)
      for key, value := range mapValue {// 對(duì)鍵和值進(jìn)行處理
      }
      
      其中key是映射的鍵,value是對(duì)應(yīng)鍵的值。對(duì)于每個(gè)鍵值對(duì),循環(huán)都會(huì)執(zhí)行一次。
    • 示例
      m := map[string]int{"a": 1, "b": 2, "c": 3}
      for key, value := range m {fmt.Printf("鍵為 %s 的值為 %d\n", key, value)
      }
      
      這個(gè)循環(huán)會(huì)遍歷映射m,并打印出每個(gè)鍵值對(duì)的鍵和值。

集合

集合分為slice和map兩種,其中slice是數(shù)組的抽象。

slice

slice是數(shù)組的抽象,Go 數(shù)組的長(zhǎng)度不可改變,在特定場(chǎng)景中這樣的集合就不太適用,Go中提供了一種靈活,功能強(qiáng)悍的內(nèi)置類型切片("動(dòng)態(tài)數(shù)組"),與數(shù)組相比切片的長(zhǎng)度是不固定的,可以追加元素,在追加時(shí)可能使切片的容量增大

方式一:聲明一個(gè)未指定大小的數(shù)組來(lái)定義切片

var identifier []type

方式二:使用make()函數(shù)來(lái)創(chuàng)建切片

var slice1 []type = make([]type, len)
也可以簡(jiǎn)寫(xiě)為
slice1 := make([]type, len)//也可以指定容量,其中capacity為可選參數(shù)
make([]T, length, capacity)

?make函數(shù)參數(shù)說(shuō)明

  • length
    • 表示切片初始的長(zhǎng)度,即切片中元素的個(gè)數(shù)。這個(gè)值決定了切片創(chuàng)建后可以直接訪問(wèn)的元素范圍。例如,如果length3,那么可以通過(guò)切片索引01、2來(lái)訪問(wèn)元素
  • capacity
    • 表示切片的容量,它是切片底層數(shù)組的大小。切片的容量必須大于等于長(zhǎng)度。容量決定了切片在不重新分配內(nèi)存的情況下能夠擴(kuò)展的最大程度。例如,如果capacity5,長(zhǎng)度為3,那么在不重新分配內(nèi)存的情況下,切片可以通過(guò)append操作最多再容納2個(gè)元素

操作方法:

append函數(shù)

newSlice := append(slice, elements...)

其中slice是原始切片,elements是要添加的一個(gè)或多個(gè)元素。append函數(shù)會(huì)返回一個(gè)新的切片,這個(gè)新切片包含了原始切片的所有元素以及新添加的元素

copy函數(shù)

/* 拷貝 numbers 的內(nèi)容到 numbers1 */copy(numbers1,numbers)

len函數(shù)

//獲取切片長(zhǎng)度
len(slice)

cap函數(shù)

//獲取切片容量
cap(slice)

切片截取:可以通過(guò)設(shè)置下限及上限來(lái)設(shè)置截取切片[lower-bound:upper-bound]

package mainimport "fmt"func main() {/* 創(chuàng)建切片 */numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}printSlice(numbers)/* 打印原始切片 */fmt.Println("numbers ==", numbers)/* 打印子切片從索引1(包含) 到索引4(不包含)*/fmt.Println("numbers[1:4] ==", numbers[1:4])/* 默認(rèn)下限為 0*/fmt.Println("numbers[:3] ==", numbers[:3])/* 默認(rèn)上限為 len(s)*/fmt.Println("numbers[4:] ==", numbers[4:])numbers1 := make([]int, 0, 5)printSlice(numbers1)/* 打印子切片從索引  0(包含) 到索引 2(不包含) */number2 := numbers[:2]printSlice(number2)/* 打印子切片從索引 2(包含) 到索引 5(不包含) */number3 := numbers[2:5]printSlice(number3)}func printSlice(x []int) {fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

運(yùn)行結(jié)果:

len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]

map

映射(map)是Go語(yǔ)言中一種用于存儲(chǔ)鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu)。以下是一些常見(jiàn)的map操作方法:

1. 創(chuàng)建map

  • 可以使用make函數(shù)創(chuàng)建map,語(yǔ)法為make(map[KeyType]ValueType),其中KeyType是鍵的類型,ValueType是值的類型。例如m := make(map[string]int)創(chuàng)建了一個(gè)鍵為字符串類型,值為整數(shù)類型的map。

2. 插入鍵值對(duì)

  • 可以直接給map賦值來(lái)插入鍵值對(duì)。例如m := make(map[string]int); m["key1"] = 1,這里將鍵為"key1",值為1的鍵值對(duì)插入到map中。

3. 訪問(wèn)鍵值對(duì)

  • 使用鍵來(lái)訪問(wèn)map中的值。例如m := make(map[string]int); m["key1"] = 1; fmt.Println(m["key1"])會(huì)打印出鍵為"key1"的值1。
  • 如果訪問(wèn)的鍵不存在于map中,會(huì)返回值類型的零值。例如m := make(map[string]int); fmt.Println(m["nonexistent_key"])會(huì)打印出整數(shù)類型的零值0。

4. 修改鍵值對(duì)

  • 可以使用鍵來(lái)修改map中的值。例如m := make(map[string]int); m["key1"] = 1; m["key1"] = 2; fmt.Println(m["key1"])會(huì)將鍵為"key1"的值從1修改為2。

5. 刪除鍵值對(duì)

  • 使用delete函數(shù)來(lái)刪除鍵值對(duì),語(yǔ)法為delete(map, key),其中map是要操作的map,key是要?jiǎng)h除的鍵。例如m := make(map[string]int); m["key1"] = 1; delete(m, "key1"); fmt.Println(m["key1"])會(huì)刪除鍵為"key1"的值,并返回整數(shù)類型的零值0,因?yàn)樵撴I值對(duì)已經(jīng)被刪除。

6. 檢查鍵是否存在

  • 可以使用一種特殊的語(yǔ)法來(lái)檢查鍵是否存在于map中,同時(shí)獲取對(duì)應(yīng)的值。例如m := make(map[string]int); m["key1"] = 1; value, exists := m["key1"]; fmt.Println(value, exists)會(huì)打印出1 true,因?yàn)殒I"key1"存在于map中,并且對(duì)應(yīng)的值為1。如果鍵不存在,exists會(huì)返回falsevalue會(huì)返回值類型的零值。

7. 遍歷map

  • 使用range來(lái)遍歷map,會(huì)依次獲取map中的鍵值對(duì)。例如m := make(map[string]int); m["key1"] = 1; m["ive had no joy with maps in GoLang!": "Here is an example of a text that could be inserted into a map, though it might not be a very practical one"]; for key, value := range m { fmt.Println(key, value) }會(huì)遍歷map中的鍵值對(duì)并打印出來(lái)。

8. map的長(zhǎng)度

  • 使用len函數(shù)可以獲取map的長(zhǎng)度,即map中鍵值對(duì)的個(gè)數(shù)。例如m := make(map[string]int); m["key1"] = 1; m["key2"] = 2; fmt.Println(len(m))會(huì)打印出2,因?yàn)檫@個(gè)map中有兩個(gè)鍵值對(duì)

結(jié)構(gòu)體

在 Go 語(yǔ)言中,結(jié)構(gòu)體(struct)是一種復(fù)合數(shù)據(jù)類型,它允許用戶將不同類型的數(shù)據(jù)組合在一起,形成一個(gè)新的類型。以下是關(guān)于結(jié)構(gòu)體的詳細(xì)介紹:

結(jié)構(gòu)體的定義

結(jié)構(gòu)體的定義一個(gè)是使用struct關(guān)鍵字,基礎(chǔ)語(yǔ)法如下:

type StructName struct {Field1 Type1Field2 Type2//...
}

其中,type是關(guān)鍵字用于定義新的類型,StructName是結(jié)構(gòu)體的名稱,struct是結(jié)構(gòu)體的關(guān)鍵字,后面跟著大括號(hào),大括號(hào)內(nèi)是結(jié)構(gòu)體的各個(gè)字段(field),每個(gè)字段由字段名和字段類型組成。例如:

type Person struct {Name stringAge  int
}

定義了一個(gè)名為Person的結(jié)構(gòu)體,它包含兩個(gè)字段:Name(字符串類型)和Age(整數(shù)類型)

結(jié)構(gòu)體變量的創(chuàng)建和初始化

  • 方法一:逐個(gè)字段初始化
    可以通過(guò)結(jié)構(gòu)體類型創(chuàng)建結(jié)構(gòu)體變量,并逐個(gè)初始化結(jié)構(gòu)體的字段。例如:
var p Person
p.Name = "Alice"
p.Age = 25
  • 方法二:使用結(jié)構(gòu)體字面量初始化
    使用結(jié)構(gòu)體字面量可以在創(chuàng)建結(jié)構(gòu)體變量的同時(shí)初始化結(jié)構(gòu)體的字段。有兩種形式:
  • 按字段順序初始化
p := Person{"Bob", 30}

這種方式要求按照結(jié)構(gòu)體定義時(shí)字段的順序提供值。

  • 指定字段名初始化
p := Person{Name: "Charlie", Age: 35}

這種方式可以不按照結(jié)構(gòu)體定義時(shí)字段的順序提供值,只要指定正確的字段名和值即可。

結(jié)構(gòu)體的嵌套

  • 結(jié)構(gòu)體可以嵌套其他結(jié)構(gòu)體。例如:
type Address struct {City    stringStreet  stringZipCode int
}type Employee struct {PersonJobTitle  stringAddress   AddressSalary    int
}

這里定義了Address結(jié)構(gòu)體和Employee結(jié)構(gòu)體,Employee結(jié)構(gòu)體嵌套了Person結(jié)構(gòu)體和Address結(jié)構(gòu)體。當(dāng)訪問(wèn)嵌套結(jié)構(gòu)體的字段時(shí),可以使用.操作符進(jìn)行多級(jí)訪問(wèn)。例如:e := Employee{Person{"David", 40}, "Engineer", Address{"New York", "5th Avenue", 10001}, 80000}; fmt.Println(e.Person.Name)會(huì)打印出David。

結(jié)構(gòu)體方法

  • 可以為結(jié)構(gòu)體定義方法,結(jié)構(gòu)體方法類似于面向?qū)ο笳Z(yǔ)言中的類方法。結(jié)構(gòu)體方法的定義語(yǔ)法如下:
func (s StructName) MethodName() ReturnType {// 方法體
}

其中,(s StructName)部分稱為接收者(receiver),它表示該方法是屬于StructName結(jié)構(gòu)體的,s是接收者變量,可以在方法體中使用接收者變量來(lái)訪問(wèn)結(jié)構(gòu)體的相關(guān)字段。例如:

func (p Person) GetName() string {return p.Name
}

定義了一個(gè)Person結(jié)構(gòu)體的方法GetName,它返回Person結(jié)構(gòu)體的Name字段的值。

結(jié)構(gòu)體的內(nèi)存布局

  • 結(jié)構(gòu)體在內(nèi)存中的布局是按照字段定義的順序依次排列的,每個(gè)字段占用一定的內(nèi)存空間。不同類型的字段在內(nèi)存中所占的空間大小不同,例如,整數(shù)類型通常占用4個(gè)字節(jié)(在32位系統(tǒng)中)或8個(gè)字節(jié)(在62位系統(tǒng)中),字符串類型的內(nèi)存占用則比較復(fù)雜,它包含一個(gè)指針和一些其他信息。
  • 當(dāng)結(jié)構(gòu)體嵌套其他結(jié)構(gòu)體時(shí),嵌套的結(jié)構(gòu)體的內(nèi)存空間也是按照其自身的字段定義順序依次排列在主結(jié)構(gòu)體的內(nèi)存空間內(nèi)。

結(jié)構(gòu)體的比較

  • 如果結(jié)構(gòu)體的所有字段都是可比較的,那么結(jié)構(gòu)體本身也是可比較的。比較兩個(gè)結(jié)構(gòu)體時(shí),會(huì)逐個(gè)比較結(jié)構(gòu)體的各個(gè)字段。例如:
p1 := Person{"Alice", 25}
p2 := Person{"Alice", 25}
if p1 == p2 {fmt.Println("p1和p2相等")
}

如果結(jié)構(gòu)體中有不可比較的字段(如切片、映射等),那么結(jié)構(gòu)體本身不可比較

接口

接口定義

  • 首先我們定義了一個(gè)接口Shape
type Shape interface {Area() float64
}

這個(gè)接口規(guī)定了任何實(shí)現(xiàn)它的類型都必須有一個(gè)Area方法,該方法沒(méi)有參數(shù)并且返回一個(gè)float64類型的值。這就像是一個(gè)合同,規(guī)定了實(shí)現(xiàn)這個(gè)接口的類型需要具備計(jì)算面積的能力。

接口實(shí)現(xiàn)

  • 然后我們有一個(gè)Rectangle結(jié)構(gòu)體:
type Rectangle struct {length float64width  float64
}

并且為這個(gè)結(jié)構(gòu)體定義了一個(gè)Area方法:

func (r Rectangle) Area() float64 {return r.length * r.width
}

通過(guò)定義這個(gè)Area方法,Rectangle結(jié)構(gòu)體滿足了Shape接口的要求,也就是說(shuō)Rectangle結(jié)構(gòu)體實(shí)現(xiàn)了Shape接口。這就好比Rectangle結(jié)構(gòu)體簽署了Shape接口這個(gè)合同,它具備了按照合同要求計(jì)算面積的能力。

接口使用

  • 作為函數(shù)參數(shù):我們定義了一個(gè)函數(shù)CalculateArea
func CalculateArea(s Shape) float64 {return s.Area()
}

在這個(gè)函數(shù)中,參數(shù)sShape接口類型。這意味著我們可以傳入任何實(shí)現(xiàn)了Shape接口的類型的值作為參數(shù)。當(dāng)我們調(diào)用這個(gè)函數(shù)時(shí),如果傳入的是Rectangle結(jié)構(gòu)體的值(因?yàn)?code>Rectangle實(shí)現(xiàn)了Shape接口),那么在函數(shù)內(nèi)部就會(huì)調(diào)用Rectangle結(jié)構(gòu)體的Area方法來(lái)計(jì)算面積。

  • 作為變量類型:我們還可以定義一個(gè)Shape接口類型的變量s
var s Shape
r := Rectangle{length: 5, width: 3}
s = r
fmt.Println(s.Area())

這里首先定義了一個(gè)Shape接口類型的變量s,然后創(chuàng)建了一個(gè)Rectangle結(jié)構(gòu)體的值r,并將r賦值給s。因?yàn)?code>Rectangle實(shí)現(xiàn)了Shape接口,所以這種賦值是合法的。最后我們可以通過(guò)s調(diào)用Area方法來(lái)計(jì)算面積,實(shí)際上是調(diào)用了Rectangle結(jié)構(gòu)體的Area方法。

這樣通過(guò)接口,我們可以實(shí)現(xiàn)不同類型之間的通用性和多態(tài)性,使得代碼更加靈活和可維護(hù)。例如,如果我們還有一個(gè)Square結(jié)構(gòu)體也實(shí)現(xiàn)了Shape接口,那么我們可以同樣使用CalculateArea函數(shù)來(lái)計(jì)算它的面積,而不需要為Square重新定義一個(gè)計(jì)算面積的函數(shù)。

面向?qū)ο蟮奶匦?/h3>

Go 語(yǔ)言雖然不是純粹的面向?qū)ο缶幊陶Z(yǔ)言,但它支持一些面向?qū)ο蟮奶匦?#xff0c;主要包括封裝、繼承和多態(tài),以下是具體介紹:

封裝性

定義:封裝是指將數(shù)據(jù)和操作數(shù)據(jù)的方法組合在一起,并對(duì)外部隱藏?cái)?shù)據(jù)的實(shí)現(xiàn)細(xì)節(jié),只提供必要的接口來(lái)訪問(wèn)和操作數(shù)據(jù)

實(shí)現(xiàn)方式:通過(guò)結(jié)構(gòu)體和方法實(shí)現(xiàn),結(jié)構(gòu)體用于定義數(shù)據(jù)結(jié)構(gòu),將相關(guān)的數(shù)據(jù)組合在一起。例如,定義一個(gè)Rectangle結(jié)構(gòu)體來(lái)表示矩形的長(zhǎng)和寬:

type Rectangle struct {length float64width float64
}

方法用于操作結(jié)構(gòu)體中的數(shù)據(jù)。例如,為Rectangle結(jié)構(gòu)體定義一個(gè)計(jì)算面積的方法:

func (r Rectangle) Area() float64 {return r.length * r.width
}

通過(guò)這種方式,Rectangle結(jié)構(gòu)體的內(nèi)部數(shù)據(jù)(長(zhǎng)和寬)被封裝起來(lái),外部只能通過(guò)Area方法來(lái)獲取矩形的面積,而無(wú)法直接訪問(wèn)長(zhǎng)和寬的數(shù)據(jù)

看下面的例子會(huì)更加清晰:

package mainimport "fmt"//定義一個(gè)結(jié)構(gòu)體
type T struct {name string
}func (t T) method1() {t.name = "new name1"
}func (t *T) method2() {t.name = "new name2"
}func main() {t := T{"old name"}fmt.Println("method1 調(diào)用前 ", t.name)t.method1()fmt.Println("method1 調(diào)用后 ", t.name)fmt.Println("method2 調(diào)用前 ", t.name)t.method2()fmt.Println("method2 調(diào)用后 ", t.name)
}

運(yùn)行結(jié)果:

method1 調(diào)用前  old name
method1 調(diào)用后  old name
method2 調(diào)用前  old name
method2 調(diào)用后  new name2

當(dāng)調(diào)用t.method1()時(shí)相當(dāng)于method1(t),實(shí)參和行參都是類型 T,可以接受。此時(shí)在method1()中的t只是參數(shù)t的值拷貝,所以method1()的修改影響不到main中的t變量。

當(dāng)調(diào)用t.method2()=>method2(t),這是將 T 類型傳給了 *T 類型,go可能會(huì)取 t 的地址傳進(jìn)去:method2(&t)。所以 method1() 的修改可以影響 t。

所以說(shuō)下面的代碼保證了封裝性:

func (t T) method1() {t.name = "new name1"
}

繼承

在Go語(yǔ)言中,雖然沒(méi)有像傳統(tǒng)面向?qū)ο笳Z(yǔ)言中那樣的類繼承機(jī)制,但可以通過(guò)結(jié)構(gòu)體嵌套實(shí)現(xiàn)類似繼承的效果。以下是詳細(xì)說(shuō)明:

基本概念

  • 繼承是一種機(jī)制,它允許一個(gè)類型(子類或派生類)繼承另一個(gè)類型(父類或基類)的屬性和方法,從而實(shí)現(xiàn)代碼的復(fù)用和擴(kuò)展。子類可以在繼承父類的基礎(chǔ)上添加自己的特性,并且可以重寫(xiě)父類的某些方法以滿足自身的需求。

Go語(yǔ)言中的實(shí)現(xiàn)方式 - 結(jié)構(gòu)體嵌套

  • 假設(shè)我們有一個(gè)Base結(jié)構(gòu)體代表基類,它具有一些屬性和方法。例如:
type Base struct {Property1 stringProperty2 int
}func (b Base) Method1() {// 基類方法的實(shí)現(xiàn)
}
  • 現(xiàn)在我們想要?jiǎng)?chuàng)建一個(gè)Derived結(jié)構(gòu)體來(lái)繼承Base結(jié)構(gòu)體的特性。我們可以通過(guò)結(jié)構(gòu)體嵌套來(lái)實(shí)現(xiàn):
type Derived struct {BaseAdditionalProperty string
}
  • 通過(guò)這種嵌套方式,Derived結(jié)構(gòu)體繼承了Base結(jié)構(gòu)體的所有屬性和方法。例如,Derived結(jié)構(gòu)體可以直接訪問(wèn)Base結(jié)構(gòu)體的Property1Property2屬性,并且可以調(diào)用Base結(jié)構(gòu)體的Method1方法。

方法重寫(xiě)(覆蓋)

  • 在Go語(yǔ)言中,雖然沒(méi)有嚴(yán)格的方法重寫(xiě)語(yǔ)法,但通過(guò)結(jié)構(gòu)體嵌套和方法定義,可以實(shí)現(xiàn)類似的效果。
  • 假設(shè)我們想要在Derived結(jié)構(gòu)體中重寫(xiě)Base結(jié)構(gòu)體的Method1方法。我們可以在Derived結(jié)構(gòu)體中重新定義一個(gè)Method1方法:
func (d Derived) Method1() {// 新的方法實(shí)現(xiàn),可能會(huì)調(diào)用原始Base結(jié)構(gòu)體的方法或者完全替代它// 例如,可以先調(diào)用原始Base結(jié)構(gòu)體的方法,然后再添加一些額外的操作d.Base.Method1()// 其他額外操作
}
  • 這樣,當(dāng)我們調(diào)用Derived結(jié)構(gòu)體的Method1方法時(shí),執(zhí)行的是我們?cè)?code>Derived結(jié)構(gòu)體中重新定義的方法,而不是Base結(jié)構(gòu)體的原始方法。

注意事項(xiàng)

  • 雖然結(jié)構(gòu)體嵌套實(shí)現(xiàn)了類似繼承的功能,但它與傳統(tǒng)的繼承機(jī)制還是有一些區(qū)別。例如,在Go語(yǔ)言中,沒(méi)有像其他語(yǔ)言中那樣的類層次結(jié)構(gòu)和多態(tài)性的嚴(yán)格語(yǔ)法定義。
  • 這種結(jié)構(gòu)體嵌套的方式在實(shí)際應(yīng)用中需要謹(jǐn)慎使用,要確保代碼的可讀性和可維護(hù)性。如果嵌套過(guò)于復(fù)雜,可能會(huì)導(dǎo)致代碼難以理解和維護(hù)。

多態(tài)

定義:多態(tài)是指不同類型的對(duì)象對(duì)同一方法調(diào)用可以表現(xiàn)出不同的行為。

實(shí)現(xiàn)方式:通過(guò)接口實(shí)現(xiàn)多態(tài),首先定義一個(gè)接口,接口中規(guī)定了一些方法,但沒(méi)有具體的實(shí)現(xiàn)。例如,定義一個(gè)Shape接口,要求實(shí)現(xiàn)Area方法:

type Shape interface {Area() float64
}

然后讓不同的結(jié)構(gòu)體實(shí)現(xiàn)這個(gè)接口。例如,RectangleSquare結(jié)構(gòu)體都可以實(shí)現(xiàn)Shape接口:

func (r Rectangle) Area() float64 {return r.length * r.width
}
func (s Square) Area() float64 {return s.sideLength * s.sideLength
}

當(dāng)使用接口類型的變量時(shí),根據(jù)實(shí)際賦值的結(jié)構(gòu)體不同,調(diào)用Area方法會(huì)表現(xiàn)出不同的行為。例如:

var s Shape
r := Rectangle{length: 5, width: 3}
s = r
fmt.Println(s.Area()) // 輸出15
s1 := Square{sideLength: 4}
s = s1
fmt.Println(s.Area()) // 輸出16

通過(guò)接口實(shí)現(xiàn)了多態(tài),使得程序可以根據(jù)不同的對(duì)象類型靈活地執(zhí)行相應(yīng)的操作。

http://m.aloenet.com.cn/news/33340.html

相關(guān)文章:

  • 蘇州吳中區(qū)做網(wǎng)站新東方教育培訓(xùn)機(jī)構(gòu)官網(wǎng)
  • wordpress恢復(fù)分類目錄seo營(yíng)銷論文
  • 自動(dòng)化東莞網(wǎng)站建設(shè)北京疫情最新消息
  • 網(wǎng)站開(kāi)發(fā)視頻壓縮上傳seo資源
  • 旅游網(wǎng)站在提高用戶體驗(yàn)方面應(yīng)做哪些工作長(zhǎng)春網(wǎng)站建設(shè)制作
  • 做吃的教程網(wǎng)站品牌整合營(yíng)銷方案
  • 典型網(wǎng)站開(kāi)發(fā)的一般流程推廣app是什么工作
  • 好看網(wǎng)站手機(jī)版批量查詢權(quán)重
  • 網(wǎng)站建設(shè)php帶數(shù)據(jù)庫(kù)模板網(wǎng)絡(luò)安全
  • 如何做網(wǎng)站鏡像百度鏈接提交入口
  • 手機(jī)網(wǎng)站有哪些類型成都網(wǎng)絡(luò)推廣
  • 網(wǎng)站建設(shè) 開(kāi)發(fā)網(wǎng)站代碼百度網(wǎng)盤(pán)官網(wǎng)
  • 怎么用家里的電腦做網(wǎng)站服務(wù)器上海seo公司排名
  • 學(xué)校網(wǎng)站建設(shè)的優(yōu)勢(shì)和不足成人用品推廣網(wǎng)頁(yè)
  • 陽(yáng)春網(wǎng)站制作寧波網(wǎng)站建設(shè)推廣平臺(tái)
  • 個(gè)人網(wǎng)站 備案 廣告seo搜索引擎優(yōu)化價(jià)格
  • 72搭建網(wǎng)站網(wǎng)頁(yè)代引流推廣公司
  • 想開(kāi)個(gè)網(wǎng)站賣衣服的怎么做常州seo收費(fèi)
  • 有交做拼多多網(wǎng)站的嗎產(chǎn)品推廣平臺(tái)有哪些
  • 自己做網(wǎng)站賣什么海南樂(lè)秀同城群軟件下載
  • 做網(wǎng)站公司南京關(guān)鍵詞免費(fèi)網(wǎng)站
  • c 做網(wǎng)站設(shè)計(jì)關(guān)鍵詞排名提高方法
  • pageadmin如何做網(wǎng)站數(shù)據(jù)分析培訓(xùn)班
  • 代還信用卡網(wǎng)站建設(shè)國(guó)內(nèi)最新十大新聞
  • .net mvc做網(wǎng)站一鍵優(yōu)化清理
  • 單縣網(wǎng)站定制網(wǎng)絡(luò)營(yíng)銷策劃的主要特點(diǎn)
  • 國(guó)外網(wǎng)站 服務(wù)器青島seo排名公司
  • 網(wǎng)頁(yè)即時(shí)聊天seo推廣怎么樣
  • 用動(dòng)物做網(wǎng)站名稱可以投放廣告的網(wǎng)站
  • 沒(méi)有備案的網(wǎng)站可以做淘寶客seo網(wǎng)絡(luò)推廣培訓(xùn)班