golang入門day3 (pointer + interface + type)

指針pointer

pointer 存儲了一個變量內存的地址

package main

import "fmt"

func main(){
	a:=1
	var  p1 *int = &a
	println(p1)

	var p2 *int //nil  空指針
	fmt.Println(p2)
	println(p2)

}

在這裏插入圖片描述

指針的賦值 , 指針指向的內容 也可以用 C語言方式進行改變

同樣 *int **int ***int …

數組指針: 指向數組的指針
指針數組: 存放指針的數組
函數指針: 指向函數的指針 ,函數名就是函數指針
指針函數: 返回指針的函數 eg:
func fun1()* [4]int //返回一個數組指針

結構體:聚合類型

Go 語言拋棄了 class 和 繼承 , 只保留了strict 和 組合
創建 :type name struct{ };
p2 := name{ };
pr := Person{ name :”老王“ , age: 40, sex: ‘woman’ }

結構體 :值類型的數據:深拷貝

怎麼淺拷貝呢? 可以用指針進行賦值就可以了

new 函數 :開闢內存空間 ,返回值是一個指針

new() 在 go語言中是 函數 ,專門創建內存,返回指針的 函數

make 、new 區別

make 用於 內建類型(slice , map, channel)的內存分配 , new用於各種內存的分配;
new(T) 函數 分配了T類型的 零值進行內存填充, 並返回其地址
而 內建函數make(T,args) 創建一個 make(T,args)返回一個非零的T類型, 而不是 *T類型。

從本質上講: 導致 make 和 new兩個函數 結果不同的原因 是因爲創建之後的初始化

p2 := new(int)
p2 是一個指針 指向 0

匿名結構體: 相當於內部類(意義不大, 沒必要用)
在這裏插入圖片描述

結構體中的成員 也稱爲字段

如果在 struct 中 採用 匿名字段 ,其實是用類型名進行字段命名的 ,因此含有匿名字段的 結構體, 不能含有相同類型的字段。

結構體嵌套 要注意: 賦值的時候 注意 結構體是值類型(深拷貝)

那麼 我們應該看需要定義 類型指針 / 類型 對象, 防止沒必要的內存創建/ 無意修改

Go 語言可以模擬的 OOP思想,但Go語言不是 面向對象的語言

GO語言 :沒有 面向對象的 三大特性,, 因爲 Go語言不含 class 對象 , 但是我們可以用 struct 進行模擬

一 : 匿名結構體 模擬實現 繼承

不定義成定義成 匿名結構體 不就成組合了嗎?我感覺這樣,到時候不就成一個對象了嗎?對吧? 暫時先這樣理解試試吧 ? 但是好像跟C中組合不太一樣

在這裏插入圖片描述

package main
 type Person struct{
 	name string
 	age  int
 }
type Student struct{
	Person // 匿名結構體  模擬實現 繼承
	school string
}

func main(){
	s1 := new(Student)
	s1.name = "海綿寶寶"
	s1.age = 18
	s1.school = "北京大學"
	println(s1.name," ", s1.age," ", s1.school)
	s2 := Student{Person{"派大星",17}, "XPU"}
	println(s2.name," ",s2.age," ", s2.school)//' ' 不能表示空格代表32ASCII
	s3 := Student{Person:Person{name:"擎天柱",age:14}, school:"地球"}
	println(s3.name," ",s3.age," ", s3.school)
	s4 := Student{Person{"大黃蜂",13}, "泰斯坦"}
	println(s4.name," ",s4.age," ",s4.school)
}

匿名對象的 字段 稱爲提升字段 , 可以通過對象直接訪問,並修改

方法 vs 函數

方法: 包含了調用者的 函數稱爲方法:簡單說 包含對象的函數稱爲方法

方法 和 函數的 定義非常相似: 方法 func關鍵字後、方法名前,加上 接收器的類型 ,方法必須通過接受者來調用,誰調用誰就是接收者, 方法的接收者可以在方法的內部 進行訪問

爲什要有方法啊??

方法和函數的意義不一樣 :
1 方法 必須有調用者,是某個類別的功能; 函數 只是一段代碼 ,隨時可以調用
2 方法名可以相同, 只要接收者(調用者)不同就可以, 而 函數名不能衝突
3 方法可以模擬實現 多態(真不愧一個爹搞出來的)

Go 模擬 method 繼承

如果一個 匿名字段(匿名結構體)實現了一個method方法,那麼包含這個匿名字段的結構體也可以調用該匿名字段的方法

同時 外層struct的方法,匿名字段/內層字段 struct 是可以對其進行重寫的(C++叫重定義, JAVA 叫重寫)

內層字段 類似於 C++的內部類 ,內部字段可以訪問外層函數/方法

方法 = 調用者 + 函數

interface 接口

在面向對象世界中: 接口定義對象的行爲 , 表示對象能做什麼,實現行爲的細節是針對對象的 (感覺像多態)

在Go 語言中: 接口 interface 是一組方法簽名,類型爲接口中的所有方法提供定義 就是在 實現接口, 與OOP思想很相似。 接口指定了 類型應該具有的方法,而類型 決定了如何去實現這些方法。

總之:
Go語言 接口:定義了具有共性的一組方法,任何其他類型只要實現了這些方法 就是實現了這個接口。
接口定義了一組方法: 如果某個對象實現了這個接口的所有方法,那麼此對象就實現了該接口。

來看看 具體實現: 真的是多態的變通啊哈哈哈 ~

在這裏插入圖片描述
來看我實現的代碼

package main

//USB  接口
type  USB  interface{
	start()// 接口定義一組具有共性的方法
	end()
}
// 鼠標
type Mouse struct {
	name string
}
//U盤
type FlashDisk struct {
	name string
}
//Mouse 的實現接口
func (m Mouse)start(){
	println("鼠標就緒,可以使用,點點點")
}

func (m Mouse)end(){
	println("鼠標結束工作 ,退出啦")
}

//U盤 實現接口
func(f FlashDisk)start(){
	println("閃迪準備就緒,可以開始傳輸數據了...")
}

func(f FlashDisk)end(){
	println("傳輸數據結束,U盤拔出")
}

//test測試函數 : 普通函數,只進行測試(完美轉發)
func testInterfae(usb USB){
	usb.start()
	usb.end()
}
func main(){
	m1 := Mouse{"鼠標"}
	println(m1.name)
	m1.start()
	m1.end()

	f1 :=FlashDisk{"閃迪"}
	println(f1.name)
	f1.start()
	f1.end()

	//testInterfae(m1)
	//testInterfae(f1)
}

原理就是這麼個原理: 但我寫的代碼的耦合性太高了 ,比如: 名字應該通過對象傳遞,而非直接在方法內定義。

需要注意的是 : 1 由接口實現類型對象賦值 得到的接口對象,不能訪問原有的“一些”字段。
2 接口可以向上訪問, 但是 不能向下訪問(e g: 接口 不可以訪問對象的name 等 ,但 對象卻可以訪問接口的方法)

接口的類型

接口的語法:
	 1   如果一個函數 接收接口類型作爲參數,那麼可以接收該接口任意實現類型的對象作爲參數,甚至可以將不同實現類對象放在接口數組裏。
	 2 	定義一個對象爲接口類型對象, 那麼可以用任意實現類的對象給其賦值
鴨子類型

長的像鴨子, 會游泳 ,會嘎嘎叫 ~
針對動態語言來說 ,是一種類型推斷策略,更關注對象如何被使在這裏插入圖片描述用。、,並不是類型本身。

雖然Go 語言是弱類型語言,採用非侵入式的方式實現接口。(非侵入式就是 : 接口的 聲明 和 實現 分離)
,提高代碼的服用,減少代碼的耦合。 、

空接口: 可以當作泛型使用,非常方便

還可以配合 map 使用 map[srting] interface{}

在這裏插入圖片描述

package main

import "fmt"

type A interface {

}
type Cat struct{
	name string
}

type Dog struct{
	name string
}

//空接口 接收任意類型數據
func test1(a A){
	fmt.Println(a)
	//println(a)可能會打印成地址
}

func test2(a interface{}){
	fmt.Println(a)
}

func main(){
	var c1 A = Cat{"小老虎"}
	c := Cat{"小貓咪"}
	d := Dog{"王二狗"}
	var A = 4
	var B = 3.14
	test1(c1)
	test1(c)
	test1(d)
	test1(A)
	test1(B)

	test2(c1)
	test2(c)
	test2(d)
	test2(3.14)
	test2(6)

	m1 := make(map[string]interface{})
	m1["name"] ="隔壁老王"
	m1["age"] =18
	m1["sex"] ="man"

	fmt.Println(m1)
}

接口允許繼承 ,更允許多繼承

接口的繼承 用嵌套來模擬實現

嵌套的接口 想使用 必須實現所有 被嵌套的接口內的方法

接口的斷言

Go語言的類型斷言 是對接口實行的操作

在這裏插入圖片描述

package main

import (
	"fmt"
	"math"
)

type Circle struct{
	path float64
}

type Square struct{
	path int
}

type AA interface {
	Area()
	CLong()
}
func (s Square)Area(){
	fmt.Println("Square Area is:",s.path * s.path)
}
func (s Square)CLong(){
	fmt.Println("Square Long is:",s.path * 4)
}
func (c Circle)Area(){
	fmt.Println("Circle Area is:",c.path * c.path * math.Pi)
}
func (c Circle)CLong(){
	fmt.Println("Circle long is:",c.path * 2 * math.Pi)
}

func getAssert(a AA){
	if ret,ok :=a.(Circle); ok{
		fmt.Println("it is Circle, path is",ret.path)
	}else if ret, ok := a.(Square);ok{
		fmt.Println("it  is Square, path is ",ret.path)
	}else if ret,ok :=a.(*Circle); ok{
		fmt.Println("it is Circle, path is",(*ret).path)
	}else if ret,ok :=a.(*Square); ok{
		fmt.Println("it  is Square, path is ",ret.path)
	}else{
		fmt.Println("i don't know what type a is")
	}
}

func testAssert2(a AA){
	switch ret := a.(type){  //a.(type) 只能用在 switch 語句中
	case Circle:
		fmt.Println("Circle",ret.path)
	case Square:
		fmt.Println("Square",ret.path)
	case * Circle:
		fmt.Println("Circle",ret.path)
	case * Square:
		fmt.Println("Square",ret.path)
	default:
		fmt.Println("i don't what type it is")
	}
}


func testAssert(a AA){
	getAssert(a)
	a.Area()
	a.CLong()
}

func main(){
	c := Circle{4}
	s := Square{4}
	d := &Circle{3}
	e := &Square{3}
	testAssert2(e)
	testAssert(d)
	testAssert(c)
	testAssert(s)

	testAssert2(e)
	testAssert2(d)
	testAssert2(c)
	testAssert2(s)
}

type

type 可以自定義一種類型 , 比如將 int 定義爲 myint

但在語法上,myint類型 和int類型 不相同 ,一次不能相互賦值。 但可以前值類型轉換後進行賦值。
在這裏插入圖片描述
給類型區別名 : type newname = typename

例如 type myInt = int 那麼 myInt 就是 int 的別名

在這裏插入圖片描述

來看一個 別名的不同表現吧 :

在這裏插入圖片描述

package main

import "fmt"

type people struct{
	name string
}

type  person = people

type  puiple struct {
	person
	people
}

func(p person) show1(){
	fmt.Println(p.name)
}

func (p people) show2(){
	fmt.Println(p.name)
}

func testShow(a puiple){
	fmt.Println(a.people.name)
	fmt.Println(a.person.name)

}
func main(){
	var s puiple
	s.person.name ="小明"
	s.people.name ="明明"
	println(s.person.name)
	println(s.people.name)
//	testShow(s)
}
/*
type myInt int
type  INT = int
func main(){
var a int =4
var a1 myInt = 5
// a = a1// 不能隱式類型轉換 ,但是可以強制類型轉換
a = int (a1)
println(a)

var cc INT = 10
fmt.Printf("%T\n",cc)
}
 */

雖然 person是people 的別名, 但看起來好像是給一個對象開了兩份空間來進行存儲的 , 我們來進行驗證一下

在這裏插入圖片描述
果然: 是兩份內存(其實也很好理解: people/person 結構體中都單獨又一個 name字段 , 而puiple結構體,含一個person結構體, 也含有一個 people 結構體, 因此 是兩份內存)

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