Go語言不是面向對象的語言,只是可以采用面向對象的思維通過一些方法來模擬面向對象。面向對象思維核心就三個點:封裝、繼承、多態
GO語言和傳統的面向對象編程有所區別,并不是純粹的面向對象語言。比如C++,Java的面向對象都是基于類的,但是Go沒有類,Go是基于struct來實現OOP特性的。Go去掉了傳統的OOP語言的方法重載、構造函數、析構函數等,但是Go仍然有著面向對象編程的繼承、封裝和多態的特性,只是實現方式比較不同。?
繼承?
繼承需要符合的關系是is-a,父類更通用,子類更具體
子類會具有父類的一般特性,也會具有自身的特性
繼承就是子類繼承父類的特征和行為,使得子類具有父類的屬性和方法。
結構體嵌套?
package mainimport "fmt"// 定義一個父類
type Person struct {name stringage int
}// 定義一個子類,Student擁有了父類所有的屬性,還有自己的特性
type Student struct {Person // 匿名字段,實現了繼承。school string
}func main() {// 1、 創建父類對象p1 := Person{name: "Autumn", age: 18}fmt.Println(p1)fmt.Println(p1.name, p1.age)// 2、創建子類s1 := Student{Person: Person{name: "小明", age: 3}, school: "清華"}fmt.Println(s1)fmt.Println(s1.Person.name, s1.Person.age, s1.school)// 3、創建子類var s2 Students2.Person.name = "張三"s2.Person.age = 3s2.school = "北大"fmt.Println(s2)// 概念:提升字段, 只有匿名字段才可以做到// Person 在Student中是一個匿名字段, Person中的屬性 name age 就是提升字段// 所有的提升字段就可以直接使用了,不同在通過匿名字段來點了var s3 Students3.name = "李四"s3.age = 4s3.school = "清華"fmt.Println(s3)fmt.Println(s3.name, s3.age)
}
????????繼承就是子類繼承父類的特征和行為,使得子類具有父類的屬性和方法,使得子類具有父類相同的行為。子類會具有父類的一般特性也會具有自身的特性。
方法&函數
Go語言中同時擁有函數和方法。一定要和其他只有方法的語言區分開?
方法:需要指定調用者,約定這個方法屬于誰的. ? 對象.方法()
函數:不需要指定調用者,定義了函數就可以直接函數名()調用
-
方法可以重名,只需要調用者不同
-
如果調用者相同則不可重名
-
func (方法調用者)(方法名)(){}
package mainimport "fmt"// 方法:可以理解為函數多了一個調用者
// 方法可以重名,不同的對象,調用的結果是不一樣的
type Dog struct {name stringage int
}// 方法定義, func 方法調用者 方法名()
// 1、方法可以重名,只需要調用者不同
// 2、如果調用者相同,則不能重名
func (dog Dog) eat() {fmt.Println("Dog eating...")
}func (dog Dog) sleep() {fmt.Println("Dog sleep...")
}type Cat struct {name stringage int
}func (cat Cat) eat() {fmt.Println("Cat eating...")
}
func (cat Cat) sleep() {fmt.Println("Cat sleep...")
}func main() {// 創建一個對象dog := Dog{name: "旺財",age: 2,}fmt.Println(dog)// 方法的調用,通過對應的結構體對象來調用dog.eat()cat := Cat{name: "喵喵",age: 1,}cat.eat()
}
?方法:
-
某個類的行為功能,需要指定調用者
-
一段獨立的代碼功能,必須要使用調用者來調用
-
多個類的方法可以重名,單個類不行
-
方法是某個類的動作
函數:
-
一段獨立的代碼功能,可以直接調用
-
命名完全不能沖突
-
函數是一個特殊的類型
方法的重寫
-
子類可以重寫父類的方法
-
子類還可以有自己新增的方法
-
子類可以訪問父類中的屬性和方法
package mainimport ("fmt"
)// 方法重寫,建立在父類和子類結構上的
type Animal struct {name stringage int
}func (animal Animal) eat() {fmt.Println(animal.name, " 正在eating...")
}
func (animal Animal) sleep() {fmt.Println(animal.name, " 正在sleeping....")
}// 子類
type Dog struct {Animal
}// 子類自己的方法
func (dog Dog) wang() {fmt.Println("wangwangwanwg~~~")
}type Cat struct {Animalcolor string // 子類可以定義自己的屬性
}// 子類重寫父類的方法 , 子類的方法名和父類同名,即可重寫父類的方法
func (cat Cat) eat() {fmt.Println(cat.name, " 正在吃cat....")
}func main() {// 定義一個子類,使用父類方法dog := Dog{Animal{name: "旺財", age: 3}}dog.eat() // 調用父類的方法dog.wang() // 調用自己擴展的方法cat := Cat{Animal{name: "煤球", age: 3}, "黑色"}cat.eat() // 如果重寫了父類的方法就是調用子類自己的方法fmt.Println(cat.color) // 調用子類自己的屬性// 子類可以操作父類的方法,父類可以操作子類的嗎?不可以// 父類不能調用子類自己的擴展的方法a := Animal{name: "大黃", age: 3}a.eat()a.sleep()
}
?接口
-
接口就是把一些共性的方法集合在一起定義
-
如果有實現類將接口定的方法全部實現了,那么就代表實現了這個接口
-
接口是方法的定義集合,不需要實現具體的方法內容?
package mainimport ("fmt"
)// 接口的定義 interface 來定義,方法太多了,要歸類,方法的集合
type USB interface { // 接口,方法的集合input() // 輸入方法output() // 輸出方法
}// 鼠標
type Mouse struct {name stringprice float64
}// 鍵盤
type KeyBoard struct {name stringprice float64
}// 結構體實現了接口的全部方法就代表實現了這個接口,否則不算實現這個接口
func (mouse Mouse) output() {fmt.Println(mouse.name, "鼠標輸出")
}
func (mouse Mouse) input() {fmt.Println(mouse.name, "鼠標輸入")
}// 結構體實現了接口的全部方法就代表實現了這個接口,否則不算實現這個接口
func (keyboard KeyBoard) output() {fmt.Println(keyboard.name, "鍵盤輸入")
}
func (keyboard KeyBoard) input() {fmt.Println(keyboard.name, "鍵盤輸入")
}// 接口的測試
func test(u USB) {u.input()u.output()
}func main() {// 通過傳入接口實現類來進行調用,這樣靈活,減少臃腫m := Mouse{name: "華為鼠標"}// test 參數 USB 類型,傳入Mouse也可以呢?因為一個結構體實現了這個接口所有的方法,那這個結構體就是這個接口類型的test(m)k := KeyBoard{name: "小米鍵盤"}test(k)// 定義高級類型 k就升級了 KeyBoard --> USB 向上轉型var usb USBusb = kfmt.Println(usb)// 接口是無法使用實現類的屬性的//fmt.Println(usb.name)
}
空接口
-
不包含任何方法:空接口沒有定義任何方法,因此它可以接受任何類型的值。
-
所有類型包括結構體都默認實現了空接口:Go 中的所有類型都隱式地滿足了空接口類型。
-
空接口可以存儲任何的類型:空接口在需要處理未知類型或者多種類型的場景下非常有用。
-
類型斷言:從空接口中取出一個值時,通常需要使用類型斷言來確定該值的具體類型,以便進行類型安全的訪問。
-
反射:空接口與反射機制緊密相關,通過反射可以檢查存儲在空接口中的值的類型,并進行相應的操作。
不包含任何方法:??
package mainimport "fmt"func doSomething(i interface{}) {// 空接口i可以接收任何類型的參數,但這里我們不能對它執行任何方法調用fmt.Println("用任何類型的值做某事")
}func main() {doSomething(42)doSomething("hello")doSomething(true)
}
所有類型默認實現了空接口:
package mainimport "fmt"func main() {// 所有這些類型都隱式地實現了空接口var i interface{} = 42 // intvar j interface{} = "hello" // stringvar k interface{} = true // boolfmt.Println(i, j, k)
}
?存儲任何類型:?
package mainimport "fmt"func storeValue(i interface{}) {// 這個函數可以使用空接口存儲任何類型的值fmt.Printf("Stored value: %v\n", i)
}func main() {storeValue(123)storeValue("Go is great!")storeValue(3.14)
}
類型斷言:
?
package mainimport "fmt"func process(i interface{}) {// 使用類型斷言將空接口中的值轉換為具體類型if s, ok := i.(string); ok {fmt.Println("是字符串類型:", s)} else {fmt.Println("不是字符串類型")}if s, ok := i.(int); ok {fmt.Println("是int類型:", s)} else {fmt.Println("不是int類型")}
}func main() {process("hello")process(42) // 這將輸出 "Not a string"
}
反射:
?
package mainimport ("fmt""reflect"
)func inspect(i interface{}) {// 使用反射檢查空接口中的值v := reflect.ValueOf(i)fmt.Printf("Type: %s, Value: %v\n", v.Type(), v.Interface())
}func main() {inspect("hello")inspect(123)inspect(3.14)
}
多態:Go語言中是模擬多態?
一個事物有多種形態?
package mainimport "fmt"// 定義接口
type Animal interface {eat()sleep()
}type Dog struct {name string
}func (dog Dog) eat() {fmt.Println(dog.name, "Doc--eat")
}
func (dog Dog) sleep() {fmt.Println(dog.name, "Doc--sleep")
}// 接口的測試
func test(animal Animal) {animal.eat()animal.sleep()
}// 多態
func main() {// Dog 兩重身份:1、Dog 2、Animal ,多態dog := Dog{name: "旺財"}dog.eat()dog.sleep()// Dog 也可以是 Animal// test 參數 Animal 類型,傳入Dog也可以呢?因為一個結構體實現了這個接口所有的方法,那這個結構體就是這個接口類型的test(dog)// 定義一個類型可以為接口類型的變量// 實際上所有實現類都可以賦值給這個對象var animal Animal // 模糊的 -- 具體化,將具體的實現類賦值給他,才有意義animal = dogtest(animal)
}
?