golang學習筆記 --類與方法
1. 型別系統[類的宣告]
型別系統:
- 一組基本型別構成的“基本型別集合”;
- “基本型別集合”上定義的一系列組合、運算、轉換方法。
型別系統包括基礎型別(byte、int、bool、float等);複合型別(陣列、結構體、指標等);可以指向任何物件的型別(Any型別,類似Java的Object型別);值語義和引用語義;面向物件型別;介面。Go大多數型別為值語義,可以給任何型別新增方法(包括內建型別,不包括指標型別)。Any型別是空介面即interface{}。
2. 結構體
結構體[類屬性的宣告]
struct的功能類似Java的class,可實現巢狀組合(類似繼承的功能) struct實際上就是一種複合型別,只是對類中的屬性進行定義賦值,並沒有對方法進行定義,方法可以隨時定義繫結到該類的物件上,更具靈活性。可利用巢狀組合來實現類似繼承的功能避免程式碼重複。
type Rect struct{ //定義矩形類 x,y float64 //型別只包含屬性,並沒有方法 width,height float64 } func (r *Rect) Area() float64{ //為Rect型別繫結Area的方法,*Rect為指標引用可以修改傳入引數的值 return r.width*r.height //方法歸屬於型別,不歸屬於具體的物件,宣告該型別的物件即可呼叫該型別的方法 }
3. 方法
1、為型別新增方法[類方法宣告],方法即為有接收者的函式 func (物件名 物件型別) 函式名(引數列表) (返回值列表) 可隨時為某個物件新增方法即為某個方法新增歸屬物件(receiver),以方法為中心 在Go語言中沒有隱藏的this指標,即顯示傳遞,形參即為this,
type Integer int func (a Integer) Less(b Integer) bool{ //表示a這個物件定義了Less這個方法,a可以為任意型別 return a<b } //型別基於值傳遞,如果要修改值需要傳遞指標 func (a *Integer) Add(b Integer){ *a+=b //通過指標傳遞來改變值 }
4. 值語義和引用語義
值型別:b的修改並不會影響a的值
引用型別:b的修改會影響a的值
Go大多型別為值語義,包括基本型別:byte,int,string等;複合型別:陣列,結構體(struct),指標等
//2、值語義和引用語義 b=a b.Modify() //值型別 var a=[3]int{1,2,3} b:=a b[1]++ fmt.Println(a,b) //a=[1,2,3] b=[1,3,3] //引用型別 a:=[3]int{1,2,3} b:=&a //b指向a,即為a的地址,對b指向的值改變實際上就是對a的改變(陣列本身就是一種地址指向) b[1]++ fmt.Println(a,*b) //a=[1,3,3] b=[1,3,3] //*b,取地址指向的值
初始化[例項化物件]
資料初始化的內建函式new()與make(),二者都是用來分配空間。區別如下:
5.1. new()
func new(Type) *Type 內建函式 new 分配空間。傳遞給new 函式的是一個型別,不是一個值。返回值是指向這個新分配的零值的指標
5.2. make()
func make(Type, size IntegerType) Type 內建函式 make 分配並且初始化 一個 slice, 或者 map 或者 chan 物件。 並且只能是這三種物件。 和 new 一樣,第一個引數是 型別,不是一個值。 但是make 的返回值就是這個型別(即使一個引用型別),而不是指標。 具體的返回值,依賴具體傳入的型別。
5.3. 示例
//建立例項 rect1:=new(Rect) //new一個物件 rect2:=&Rect{} //為賦值預設值,bool預設值為false,int預設為零值0,string預設為空字串 rect3:=&Rect{0,0,100,200} //取地址並賦值,按宣告的變數順序依次賦值 rect4:=&Rect{width:100,height:200} //按變數名賦值不按順序賦值 //建構函式:沒有構造引數的概念,通常由全域性的建立函式NewXXX來實現建構函式的功能 func NewRect(x,y,width,height float64) *Rect{ return &Rect{x,y,width,height} //利用指標來改變傳入引數的值達到類似構造引數的效果 } //方法的過載,Go不支援方法的過載(函式同名,引數不同) //v …interface{}表示引數不定的意思,其中v是slice型別,及宣告不定引數,可以傳入任意引數,實現類似方法的過載 func (poem *Poem) recite(v ...interface{}) { fmt.Println(v) }
package main import "fmt" type Rect struct { //定義矩形類 x, y float64 //型別只包含屬性,並沒有方法 width, height float64 } func (r *Rect) Area() float64 { //為Rect型別繫結Area的方法,*Rect為指標引用可以修改傳入引數的值 return r.width * r.height //方法歸屬於型別,不歸屬於具體的物件,宣告該型別的物件即可呼叫該型別的方法 } func main() { rect1 := new(Rect) //new一個物件 rect2 := &Rect{} //為賦值預設值,bool預設值為false,int預設為零值0,string預設為空字串 rect3 := &Rect{0, 0, 100, 200} //取地址並賦值,按宣告的變數順序依次賦值 rect4 := &Rect{width: 100, height: 200} //按變數名賦值不按順序賦值 fmt.Println(rect1.Area()) fmt.Println(rect2.Area()) fmt.Println(rect3.Area()) fmt.Println(rect4.Area()) }
6. 匿名組合[繼承]
組合,即方法代理,例如A包含B,即A通過訊息傳遞的形式代理了B的方法,而不需要重複寫B的方法。
繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴充套件。繼承主要為了程式碼複用,繼承也可以擴充套件已存在的程式碼模組(類)。
嚴格來講,繼承是“a kind of ”,即子類是父類的一種,例如student是person的一種;組合是“a part of”,即父類是子類中的一部分,例如眼睛是頭部的一部分。
//1、匿名組合的方式實現了類似Java繼承的功能,可以實現多繼承 type Base struct{ Name string } func (base *Base) Foo(){...} //Base的Foo()方法 func (base *Base) Bar(){...} //Base的Bar()方法 type Foo struct{ Base //通過組合的方式聲明瞭基類,即繼承了基類 ... } func (foo *Foo) Bar(){ foo.Base.Bar() //並改寫了基類的方法,該方法實現時先呼叫基類的Bar()方法 ... //如果沒有改寫即為繼承,呼叫foo.Foo()和呼叫foo.Base.Foo()的作用的一樣的 } //修改記憶體佈局 type Foo struct{ ... //其他成員資訊 Base } //以指標方式組合 type Foo struct{ *Base //以指標方式派生,建立Foo例項時,需要外部提供一個Base類例項的指標 ... } //名字衝突問題,組合內外如果出現名字重複問題,只會訪問到最外層,內層會被隱藏,不會報錯,即類似java中方法覆蓋/重寫。 type X struct{ Name string } type Y struct{ X //Y.X.Name會被隱藏,內層會被隱藏 Name string //只會訪問到Y.Name,只會呼叫外層屬性 }
package main import "fmt" type X struct { Name string } type Y struct { X //Y.X.Name會被隱藏,內層會被隱藏 Name string //只會訪問到Y.Name,只會呼叫外層屬性 } func main() { a := X{"Mary"} b := Y{a, "Tom"} fmt.Println(b.X.Name) fmt.Println(b.Name) }
輸出:
Mary
Tom
7. 可見性[封裝]
封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。
封裝的本質或目的其實程式對資訊(資料)的控制力。封裝分為兩部分:該隱藏的隱藏,該暴露的暴露。封裝可以隱藏實現細節,使得程式碼模組化。
Go中用大寫字母開頭來表示public,可以包外訪問;小寫字母開頭來表示private,只能包內訪問;訪問性是包級別非型別級別,如果可訪問性是型別一致的,可以加friend關鍵字表示朋友關係可互相訪問彼此的私有成員(屬性和方法)
type Rect struct{ X,Y float64 Width,Height float64 //字母大寫開頭表示該屬性可以由包外訪問到 } func (r *Rect) area() float64{ //字母小寫開頭表示該方法只能包內呼叫 return r.Width*r.Height }