Go
文章目錄
- Go
- 變量
- 指針
- Go函數
- Go面向對象
- 繼承
- 接口
- 資料結構
- 連結清單
變量
變量聲明
//1、單變量聲明,類型放在變量名之後,可以為任意類型
var 變量名 類型
var v1,v2,v3 string //多變量同類型聲明
//2、多變量聲明
var {
v1 int
v2 []int
}
變量初始化
//1、使用關鍵字var,聲明變量類型并指派
var v1 int=10
//2、使用關鍵字var,直接對變量指派,go可以自動推導出變量類型
var v2=10
//3、直接使用“:=”對變量指派,不使用var,兩者同時使用會文法沖突,推薦使用
v3:=10
匿名變量
//Go中所有聲明後的變量都需要調用到,當出現函數多傳回值,并且部分傳回值不需要使用時,可以使用匿名變量丢棄該傳回值
func GetName()(firstName,lastName,nickName string){
return "May","Chan","Make"
}
_,_,nickName:=GetName() //使用匿名變量丢棄部分傳回值
指針
指針(pointer)在Go語言中可以被拆分為兩個核心概念:
- 類型指針,允許對這個指針類型的資料進行修改,傳遞資料可以直接使用指針,而無須拷貝資料,類型指針不能進行偏移和運算。(類似Java中的引用)
- 切片,由指向起始元素的原始指針、元素數量和容量組成。
每個變量在運作時都擁有一個位址,這個位址代表變量在記憶體中的位置。Go語言中使用在變量名前面添加
&
操作符(字首)來擷取變量的記憶體位址(取位址操作)
取位址操作符
&
和取值操作符
*
是一對互補操作符,
&
取出位址,
*
根據位址取出位址指向的值。
func main() {
str := "test ptr"
fmt.Println(&str) // &T 取變量T的記憶體位址值指派到ptr 0xc000010200 傳回的是指針類型
fmt.Println(*&str) // *ptr 取指定記憶體位址對應的變量值 test ptr 傳回的是值類型
}
變量、指針位址、指針變量、取位址、取值的互相關系和特性如下:
- 對變量進行取位址操作使用
操作符,可以獲得這個變量的指針變量。&
- 指針變量的值是指針位址。
- 對指針變量進行取值操作使用
操作符,可以獲得指針變量指向的原變量的值。*
通過指針修改資料
// 交換函數
func swap1(a, b *int) {
// 取a指針的值, 賦給臨時變量t
t := *a
// 取b指針的值, 賦給a指針指向的變量
*a = *b
// 将a指針的值賦給b指針指向的變量
*b = t
}
func main() {
// 準備兩個變量, 指派1和2
x, y := 1, 2
// 交換變量值
swap1(&x, &y)
// 輸出變量值
fmt.Println(x, y)
}
new() 函數可以建立一個對應類型的指針,建立過程會配置設定記憶體,被建立的指針指向預設值。
func new(Type) *Type
// new() 建立指針的另一種手段
str := new(string)
*str = "test new"
fmt.Println(str) // 0xc000010200
fmt.Println(*str)
go和java類似,都是值傳遞,修改的是傳入值的副本
type Person struct {
name string
age int
}
func updateAge(p Person){
p.age = 10
}
func updateAgeByPtr(p *Person) {
p.age = 10
}
func main() {
p := Person{age: 0,name: "kevin"}
fmt.Println(p.age)
updateAge(p) // go中是值傳遞
fmt.Println(p.age)
updateAgeByPtr(&p)
fmt.Println(p.age)
}
變量聲明而沒有指派,預設為零值,不同類型零值不同,例如字元串零值為空字元串;
指針聲明而沒有指派,預設為nil,即該指針沒有任何指向。當指針沒有指向的時候,不能對(*point)進行操作包括讀取,否則會報空指針異常。
var aPot *string
fmt.Println(aPot) // <nil>
指針也是一種類型,不同于一般類型,指針的值是位址,這個位址指向其他的記憶體,通過指針可以讀取其所指向的位址所存儲的值。
函數方法的接受者,也可以是指針變量。無論普通接受者還是指針接受者都會被拷貝傳入方法中,不同在于拷貝的指針,其指向的地方都一樣,隻是其自身的位址不一樣。
Go函數
Go函數中隻關注匿名函數和閉包
所謂閉包就是函數的傳回值是也是一個函數
// case1
func foo1(x *int) func() {
// 傳回值是一個函數
return func() {
*x = *x + 1
fmt.Printf("foo1 val = %d\n", *x)
}
}
// case2
func foo2(x int) func() {
return func() {
x = x + 1
fmt.Printf("foo1 val = %d\n", x)
}
}
func main() {
x := 133
f1 := foo1(&x)
f1()
fmt.Println(x)
y := 100
f2 := foo2(y)
f2()
fmt.Println(y)
}
執行結果:
foo1 val = 134
134
foo1 val = 101
100
case1和case2的差別在于值傳遞和引用傳遞的差別,go和Java一樣隻有值傳遞,這裡所說的引用傳遞是傳遞的是位址的值
閉包的延遲綁定
隻有在執行閉包函數的時候才會去尋找最新的函數環境
// case7 閉包的延遲綁定,
func foo7(x int) []func() {
var fs []func()
values := []int{1, 2, 3, 4, 5}
for _, val := range values {
fs = append(fs, func() {
fmt.Printf("foo7 val = %d\n", x+val)
})
}
return fs
}
func main() {
fs := foo7(11)
for _, f := range fs{
f()
}
}
答案是:(在用到的時候去尋找函數的最新環境,此時是val 是 5)
foo7 val = 16
foo7 val = 16
foo7 val = 16
foo7 val = 16
Go面向對象
繼承
在Go語言中,并沒有顯式的繼承與顯式的接口實作(接口實作其實也算是一種繼承),Go對于繼承,是通過組合來實作的
// 繼承
type People struct {
name string
age int
}
type Teacher struct {
People // 以組合的方式引入
teacherSomeThing string
}
接口
go中的接口不能有變量,go中實作接口是基于方法,如果一個類實作了這個接口中所有的方法,那麼這個類就實作了這個接口
import "fmt"
// 隻有實作了接口中的所有方法才是實作了這個接口
type Usb interface {
start()
stop()
}
type Usb1 interface {
start()
stop()
}
// computer既實作了Usb1也實作了Usb接口
type computer struct {
}
// 實作start
func (c computer)start() {
fmt.Println("調用start")
}
// 實作stop
func (c computer) stop() {
fmt.Println("調用stop")
}
func testUsb(u Usb) {
u.start()
u.stop()
}
func main() {
c := computer{}
testUsb(c)
}
// 内置的error接口,是一個正常的用于處理錯誤的接口。
// 其中 nil 表示沒有錯誤。
type error interface {
Error() string
}
type MyError struct {
errorMsg string
}
func (error MyError) Error() string{
return error.errorMsg
}
func main() {
myError := MyError{
errorMsg : "catch error",
}
fmt.Println(myError.Error())
}