一個程式就是一個世界,有很多物件(變數)
Golang 語言面向物件編程說明
-
Golang 也支持面向物件編程(OOP),但是和傳統的面向物件編程有區別,并不是純粹的面向對
象語言,所以我們說 Golang 支持面向物件編程特性是比較準確的, -
Golang 沒有類(class),Go 語言的結構體(struct)和其它編程語言的類(class)有同等的地位,你可
以理解 Golang 是基于 struct 來實作 OOP 特性的, -
Golang 面向物件編程非常簡潔,去掉了傳統 OOP 語言的繼承、方法多載、建構式和析構函
數、隱藏的 this 指標等等 -
Golang 仍然有面向物件編程的 繼承,封裝和多型的特性,只是實作的方式和其它 OOP 語言不
一樣,比如繼承 :Golang 沒有 extends 關鍵字,繼承是通過匿名欄位來實作, -
Golang 面向物件(OOP)很優雅,OOP 本身就是語言型別系統(type system)的一部分,通過介面
(interface)關聯,耦合性低,也非常靈活,也就是說在 Golang 中面
向介面編程是非常重要的特性,
結構體與結構體變數(實體/物件)的關系
- 將一類事物的特性提取出來(比如貓類), 形成一個新的資料型別, 就是一個結構體,
- 通過這個結構體,我們可以創建多個變數(實體/物件)
- 事物可以貓類,也可以是 Person , Fish 或是某個工具類,,,
package main
import (
"fmt"
)
//定義一個Cat結構體,將Cat的各個欄位/屬性資訊,放入到Cat結構體進行管理
type Cat struct {
Name string
Age int
Color string
Hobby string
Scores [3]int // 欄位是陣列...
}
func main() {
// 張老太養了20只貓貓:一只名字叫小白,今年3歲,白色,還有一只叫小花,
// 今年100歲,花色,請撰寫一個程式,當用戶輸入小貓的名字時,就顯示該貓的名字,
// 年齡,顏色,如果用戶輸入的小貓名錯誤,則顯示 張老太沒有這只貓貓,
// //1. 使用變數的處理
// var cat1Name string = "小白"
// var cat1Age int = 3
// var cat1Color string = "白色"
// var cat2Name string = "小花"
// var cat2Age int = 100
// var cat2Color string = "花色"
// //2. 使用陣列解決
// var catNames [2]string = [...]string{"小白", "小花"}
// var catAges [2]int = [...]int{3, 100}
// var catColors [2]string = [...]string{"白色", "花色"}
// //... map[string]string
// fmt.Println("ok")
// 使用struct來完成案例
// 創建一個Cat的變數
var cat1 Cat // var a int
fmt.Printf("cat1的地址=%p\n", &cat1)
cat1.Name = "小白"
cat1.Age = 3
cat1.Color = "白色"
cat1.Hobby = "吃<?)))><<"
fmt.Println("cat1=", cat1)
fmt.Println("貓貓的資訊如下:")
fmt.Println("name=", cat1.Name)
fmt.Println("Age=", cat1.Age)
fmt.Println("color=", cat1.Color)
fmt.Println("hobby=", cat1.Hobby)
}
結構體和結構體變數(實體)的區別和聯系
通過上面的案例和講解我們可以看出:
- 結構體是自定義的資料型別,代表一類事物.
- 結構體變數(實體)是具體的,實際的,代表一個具體變數
結構體變數(實體)在記憶體的布局(重要!)
如何宣告結構體
基本語法
type 結構體名稱 struct {
field1 type
field2 type
}
//舉例:
type Student struct {
Name string //欄位
Age int //欄位
Score float32
}
欄位/屬性
基本介紹
- 從概念或叫法上看: 結構體欄位 = 屬性 = field (即授課中,統一叫欄位)
- 欄位是結構體的一個組成部分,一般是 基本資料型別、 陣列,也可是 參考型別,比如我們前面定
義貓結構體 的 Name string 就是屬性
注意事項和細節說明
- 欄位宣告語法同變數,示例:欄位名 欄位型別
- 欄位的型別可以為:基本型別、陣列或參考型別
- 在創建一個結構體變數后,如果沒有給欄位賦值,都對應一個零值(默認值),規則同前面講的
一樣:
布爾型別是 false ,數值是 0 ,字串是 "",
陣列型別的默認值和它的元素型別相關,比如 score [3]int 則為[0, 0, 0]
指標,slice ,和 map 是 的零值都是 nil ,即還沒有分配空間,
4)不同結構體變數的欄位是獨立,互不影響,一個結構體變數欄位的更改,不影響另外一個, 結構體
是值型別,
package main
import (
"fmt"
)
//如果結構體的欄位型別是: 指標,slice,和map的零值都是 nil ,即還沒有分配空間
//如果需要使用這樣的欄位,需要先make,才能使用.
type Person struct{
Name string
Age int
Scores [5]float64
ptr *int //指標
slice []int //切片
map1 map[string]string //map
}
type Monster struct{
Name string
Age int
}
func main() {
//定義結構體變數
var p1 Person
fmt.Println(p1)
if p1.ptr == nil {
fmt.Println("ok1")
}
if p1.slice == nil {
fmt.Println("ok2")
}
if p1.map1 == nil {
fmt.Println("ok3")
}
//使用slice, 再次說明,一定要make
p1.slice = make([]int, 10)
p1.slice[0] = 100 //ok
//使用map, 一定要先make
p1.map1 = make(map[string]string)
p1.map1["key1"] = "tom~"
fmt.Println(p1)
//不同結構體變數的欄位是獨立,互不影響,一個結構體變數欄位的更改,
//不影響另外一個, 結構體是值型別
var monster1 Monster
monster1.Name = "牛魔王"
monster1.Age = 500
monster2 := monster1 //結構體是值型別,默認為值拷貝
monster2.Name = "青牛精"
fmt.Println("monster1=", monster1) //monster1= {牛魔王 500}
fmt.Println("monster2=", monster2) //monster2= {青牛精 500}
}
創建結構體變數和訪問結構體欄位
方式 1-直接宣告
案例演示: var person Person
前面我們已經說了,
方式 2-{}
案例演示: var person Person = Person{}
方式 3-&
案例: var person *Person = new (Person)
方式 4-{}
案例: var person *Person = &Person{}
說明:
- 第 3 種和第 4 種方式回傳的是 結構體指標,
- 結構體指標訪問欄位的標準方式應該是:(結構體指標).欄位名 ,比如 (person).Name = "tom"
- 但 go 做了一個簡化,持 也支持 結構體指標. 欄位名, 比如 person.Name = "tom",更加符合程式員
使用的習慣,go 層 編譯器底層 對 對 person.Name 化 做了轉化 (*person).Name
package main
import (
"fmt"
)
type Person struct{
Name string
Age int
}
func main() {
//方式1
//方式2
p2 := Person{"mary", 20}
// p2.Name = "tom"
// p2.Age = 18
fmt.Println(p2)
//方式3-&
//案例: var person *Person = new (Person)
var p3 *Person= new(Person)
//因為p3是一個指標,因此標準的給欄位賦值方式
//(*p3).Name = "smith" 也可以這樣寫 p3.Name = "smith"
//原因: go的設計者 為了程式員使用方便,底層會對 p3.Name = "smith" 進行處理
//會給 p3 加上 取值運算 (*p3).Name = "smith"
(*p3).Name = "smith"
p3.Name = "john" //
(*p3).Age = 30
p3.Age = 100
fmt.Println(*p3)
//方式4-{}
//案例: var person *Person = &Person{}
//下面的陳述句,也可以直接給字符賦值
//var person *Person = &Person{"mary", 60}
var person *Person = &Person{}
//因為person 是一個指標,因此標準的訪問欄位的方法
// (*person).Name = "scott"
// go的設計者為了程式員使用方便,也可以 person.Name = "scott"
// 原因和上面一樣,底層會對 person.Name = "scott" 進行處理, 會加上 (*person)
(*person).Name = "scott"
person.Name = "scott~~"
(*person).Age = 88
person.Age = 10
fmt.Println(*person)
}
struct 型別的記憶體分配機制
略
結構體使用注意事項和細節
- 結構體的所有欄位在 記憶體中是連續的
package main
import "fmt"
//結構體
type Point struct {
x int
y int
}
//結構體
type Rect struct {
leftUp, rightDown Point
}
//結構體
type Rect2 struct {
leftUp, rightDown *Point
}
func main() {
r1 := Rect{Point{1,2}, Point{3,4}}
//r1有四個int, 在記憶體中是連續分布
//列印地址
fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n",
&r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
//r2有兩個 *Point型別,這個兩個*Point型別的本身地址也是連續的,
//但是他們指向的地址不一定是連續
r2 := Rect2{&Point{10,20}, &Point{30,40}}
//列印地址
fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n",
&r2.leftUp, &r2.rightDown)
//他們指向的地址不一定是連續..., 這個要看系統在運行時是如何分配
fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n",
r2.leftUp, r2.rightDown)
}
- 結構體是用戶單獨定義的型別,和其它型別進行轉換時需要有完全相同的欄位(名字、個數和類
型) - 結構體進行 type 重新定義(相當于取別名),Golang 認為是新的資料型別,但是相互間可以強轉
- struct 的每個欄位上,可以寫上一個 tag, 該 tag 可以通過反射機制獲取,常見的使用場景就是 序
列化和反序列化
package main
import "fmt"
import "encoding/json"
type A struct {
Num int
}
type B struct {
Num int
}
type Monster struct{
Name string `json:"name"` // `json:"name"` 就是 struct tag
Age int `json:"age"`
Skill string `json:"skill"`
}
func main() {
var a A
var b B
a.Num=1
b.Num=2
a = A(b) // ? 可以轉換,但是有要求,就是結構體的的欄位要完全一樣(包括:名字、個數和型別!)
fmt.Println(a, b)
//1. 創建一個Monster變數
monster := Monster{"牛魔王", 500, "芭蕉扇~"}
//2. 將monster變數序列化為 json格式字串
// json.Marshal 函式中使用反射,反射時,詳細介紹
jsonStr, err := json.Marshal(monster)
if err != nil {
fmt.Println("json 處理錯誤 ", err)
}
fmt.Println("jsonStr", string(jsonStr))
}
方法
基本介紹
在某些情況下,我們要需要宣告(定義)方法,比如 Person 結構體:除了有一些欄位外( 年齡,姓
名..),Person 結構體還有一些行為比如:可以說話、跑步..,通過學習,還可以做算術題,這時就要用方法才能完成,
Golang 中的方法是 作用在指定的資料型別上的(即:和指定的資料型別系結),因此 自定義型別,
都可以有方法,而不僅僅是 struct,
方法的宣告和呼叫
typeAstruct {
Num int
}
func (aA) test() {
fmt.Println(a.Num)
}
** 對上面的語法的說明**
- func (aA) test() {} 表示 A 結構體有一方法,方法名為 test
- (aA) 體現 test 方法是和 A 型別系結的
package main
import (
"fmt"
)
type Person struct {
Name string
}
//函式
//對于普通函式,接收者為值型別時,不能將指標型別的資料直接傳遞,反之亦然
func test01(p Person) {
fmt.Println(p.Name)
}
func test02(p *Person) {
fmt.Println(p.Name)
}
//對于方法(如struct的方法),
//接收者為值型別時,可以直接用指標型別的變數呼叫方法,反過來同樣也可以
func (p Person) test03() {
p.Name = "jack"
fmt.Println("test03() =", p.Name) // jack
}
func (p *Person) test04() {
p.Name = "mary"
fmt.Println("test03() =", p.Name) // mary
}
func main() {
p := Person{"tom"}
test01(p)
test02(&p)
p.test03()
fmt.Println("main() p.name=", p.Name) // tom
(&p).test03() // 從形式上是傳入地址,但是本質仍然是值拷貝
fmt.Println("main() p.name=", p.Name) // tom
(&p).test04()
fmt.Println("main() p.name=", p.Name) // mary
p.test04() // 等價 (&p).test04 , 從形式上是傳入值型別,但是本質仍然是地址拷貝
}
對上面的總結
- test 方法和 Person 型別系結
- test 方法只能通過 Person 型別的變數來呼叫,而不能直接呼叫,也不能使用其它型別變數來調
用 - func (p Person) test() {}... p 表示哪個 Person 變數呼叫,這個 p 就是它的副本, 這點和函式傳參非
常相似, - p 這個名字,有程式員指定,不是固定, 比如修改成 person 也是可以
方法快速入門
- 給 Person 結構體添加 speak 方法,輸出 xxx 是一個好人
- 給 Person 結構體添加 jisuan 方法,可以計算從 1+..+1000 的結果, 說明方法體內可以函式一樣,
進行各種運算 - 給 Person 結構體 jisuan2 方法,該方法可以接收一個數 n,計算從 1+..+n 的結果
- 給 Person 結構體添加 getSum 方法,可以計算兩個數的和,并回傳結果
- 方法的呼叫
package main
import (
"fmt"
)
type Person struct{
Name string
}
//給Person結構體添加speak 方法,輸出 xxx是一個好人
func (p Person) speak() {
fmt.Println(p.Name, "是一個goodman~")
}
//給Person結構體添加jisuan 方法,可以計算從 1+..+1000的結果,
//說明方法體內可以函式一樣,進行各種運算
func (p Person) jisuan() {
res := 0
for i := 1; i <= 1000; i++ {
res += i
}
fmt.Println(p.Name, "計算的結果是=", res)
}
//給Person結構體jisuan2 方法,該方法可以接收一個引數n,計算從 1+..+n 的結果
func (p Person) jisuan2(n int) {
res := 0
for i := 1; i <= n; i++ {
res += i
}
fmt.Println(p.Name, "計算的結果是=", res)
}
//給Person結構體添加getSum方法,可以計算兩個數的和,并回傳結果
func (p Person) getSum(n1 int, n2 int) int {
return n1 + n2
}
//給Person型別系結一方法
func (person Person) test() {
person.Name = "jack"
fmt.Println("test() name=", person.Name) // 輸出jack
}
type Dog struct {
}
func main() {
var p Person
p.Name = "tom"
p.test() //呼叫方法
fmt.Println("main() p.Name=", p.Name) //輸出 tom
//下面的使用方式都是錯誤的
// var dog Dog
// dog.test()
// test()
//呼叫方法
p.speak()
p.jisuan()
p.jisuan2(20)
n1 := 10
n2 := 20
res := p.getSum(n1, n2)
fmt.Println("res=", res)
}
方法的呼叫和傳參機制原理:(重要!)
說明:
方法的呼叫和傳參機制和函式基本一樣,不一樣的地方是方法呼叫時,會將呼叫方法的變數,當做
實參也傳遞給方法,下面我們舉例說明,
說明:
- 在通過一個變數去呼叫方法時,其呼叫機制和函式一樣
- 不一樣的地方時,變數呼叫方法時,該變數本身也會作為一個引數傳遞到方法(如果變數是值類
型,則進行值拷貝,如果變數是參考型別,則進行地質拷貝)
案例 2
請撰寫一個程式,要求如下:
- 宣告一個結構體 Circle, 欄位為 radius
- 宣告一個方法 area 和 Circle 系結,可以回傳面積,
package main
import (
"fmt"
)
type Circle struct {
radius float64
}
//2)宣告一個方法area和Circle系結,可以回傳面積,
func (c Circle) area() float64 {
return 3.14 * c.radius * c.radius
}
//為了提高效率,通常我們方法和結構體的指標型別系結
func (c *Circle) area2() float64 {
//因為 c是指標,因此我們標準的訪問其欄位的方式是 (*c).radius
//return 3.14 * (*c).radius * (*c).radius
// (*c).radius 等價 c.radius
fmt.Printf("c 是 *Circle 指向的地址=%p", c)
c.radius = 10
return 3.14 * c.radius * c.radius
}
func main() {
// 1)宣告一個結構體Circle, 欄位為 radius
// 2)宣告一個方法area和Circle系結,可以回傳面積,
// 3)提示:畫出area執行程序+說明
//創建一個Circle 變數
// var c Circle
// c.radius = 4.0
// res := c.area()
// fmt.Println("面積是=", res)
//創建一個Circle 變數
var c Circle
fmt.Printf("main c 結構體變數地址 =%p\n", &c)
c.radius = 7.0
//res2 := (&c).area2()
//編譯器底層做了優化 (&c).area2() 等價 c.area()
//因為編譯器會自動的給加上 &c
res2 := c.area2()
fmt.Println("面積=", res2)
fmt.Println("c.radius = ", c.radius) //10
}
方法的宣告(定義)
func (recevier type) methodName(引數串列) (回傳值串列){
方法體
return 回傳值
}
- 引數串列:表示方法輸入
- recevier type : 表示這個方法和 type 這個型別進行系結,或者說該方法作用于 type 型別
- receiver type : type 可以是結構體,也可以其它的自定義型別
- receiver : 就是 type 型別的一個變數(實體),比如 :Person 結構體 的一個變數(實體)
- 回傳值串列:表示回傳的值,可以多個
- 方法主體:表示為了 實作某一功能代碼塊
- return 陳述句不是必須的,
方法的注意事項和細節
- 結構體型別是值型別,在方法呼叫中,遵守值型別的傳遞機制,是值拷貝傳遞方式
- 如程式員希望在方法中,修改結構體變數的值,可以通過結構體指標的方式來處理
- Golang 中的方法作用在指定的資料型別上的(即:和指定的資料型別系結),因此自定義型別,
都可以有方法,而不僅僅是 struct, 比如 int , float32 等都可以有方法
package main
import (
"fmt"
)
/*
Golang中的方法作用在指定的資料型別上的(即:和指定的資料型別系結),因此自定義型別,
都可以有方法,而不僅僅是struct, 比如int , float32等都可以有方法
*/
type integer int
func (i integer) print() {
fmt.Println("i=", i)
}
//撰寫一個方法,可以改變i的值
func (i *integer) change() {
*i = *i + 1
}
type Student struct {
Name string
Age int
}
//給*Student實作方法String()
func (stu *Student) String() string {
str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
return str
}
func main() {
var i integer = 10
i.print()
i.change()
fmt.Println("i=", i)
//定義一個Student變數
stu := Student{
Name : "tom",
Age : 20,
}
//如果你實作了 *Student 型別的 String方法,就會自動呼叫
fmt.Println(&stu)
}
幾個小例子
1)撰寫結構體(MethodUtils),編程一個方法,方法不需要引數,在方法中列印一個 108 的矩形,
在 main 方法中呼叫該方法
2)撰寫一個方法,提供 m 和 n 兩個引數,方法中列印一個 mn 的矩形
3) 撰寫一個方法算該矩形的面積(可以接收長 len,和寬 width), 將其作為方法回傳值,在 main
方法中呼叫該方法,接識訓傳的面積值并列印,
4) 撰寫方法:判斷一個數是奇數還是偶數
5) 根據行、列、字符列印 對應行數和列數的字符,比如:行:3,列:2,字符*,則列印相應的效
果
6) 定義小小計算器結構體(Calcuator),實作加減乘除四個功能
實作形式 1:分四個方法完成:
實作形式 2:用一個方法搞定
package main
import (
"fmt"
)
type MethodUtils struct {
//欄位...
}
//給MethodUtils撰寫方法
func (mu MethodUtils) Print() {
for i := 1; i <= 10; i++ {
for j := 1; j <= 8; j++ {
fmt.Print("*")
}
fmt.Println()
}
}
//2)撰寫一個方法,提供m和n兩個引數,方法中列印一個m*n的矩形
func (mu MethodUtils) Print2(m int, n int) {
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
fmt.Print("*")
}
fmt.Println()
}
}
/*
撰寫一個方法算該矩形的面積(可以接收長len,和寬width),
將其作為方法回傳值,在main方法中呼叫該方法,接識訓傳的面積值并列印
*/
func (mu MethodUtils) area(len float64, width float64) (float64) {
return len * width
}
/*
撰寫方法:判斷一個數是奇數還是偶數
*/
func (mu *MethodUtils) JudgeNum(num int) {
if num % 2 == 0 {
fmt.Println(num, "是偶數..")
} else {
fmt.Println(num, "是奇數..")
}
}
/*
根據行、列、字符列印 對應行數和列數的字符,
比如:行:3,列:2,字符*,則列印相應的效果
*/
func (mu *MethodUtils) Print3(n int, m int, key string) {
for i := 1; i <= n ; i++ {
for j := 1; j <= m; j++ {
fmt.Print(key)
}
fmt.Println()
}
}
/*
定義小小計算器結構體(Calcuator),
實作加減乘除四個功能
實作形式1:分四個方法完成: , 分別計算 + - * /
實作形式2:用一個方法搞定, 需要接收兩個數,還有一個運算子
*/
//實作形式1
type Calcuator struct{
Num1 float64
Num2 float64
}
func (calcuator *Calcuator) getSum() float64 {
return calcuator.Num1 + calcuator.Num2
}
func (calcuator *Calcuator) getSub() float64 {
return calcuator.Num1 - calcuator.Num2
}
//..
//實作形式2
func (calcuator *Calcuator) getRes(operator byte) float64 {
res := 0.0
switch operator {
case '+':
res = calcuator.Num1 + calcuator.Num2
case '-':
res = calcuator.Num1 - calcuator.Num2
case '*':
res = calcuator.Num1 * calcuator.Num2
case '/':
res = calcuator.Num1 / calcuator.Num2
default:
fmt.Println("運算子輸入有誤...")
}
return res
}
func main() {
/*
1)撰寫結構體(MethodUtils),編程一個方法,方法不需要引數,
在方法中列印一個10*8 的矩形,在main方法中呼叫該方法,
*/
var mu MethodUtils
mu.Print()
fmt.Println()
mu.Print2(5, 20)
areaRes := mu.area(2.5, 8.7)
fmt.Println()
fmt.Println("面積為=", areaRes)
mu.JudgeNum(11)
mu.Print3(7, 20, "@")
//測驗一下:
var calcuator Calcuator
calcuator.Num1 = 1.2
calcuator.Num2 = 2.2
fmt.Printf("sum=%v\n", fmt.Sprintf("%.2f",calcuator.getSum()))
fmt.Printf("sub=%v\n",fmt.Sprintf("%.2f",calcuator.getSub()))
res := calcuator.getRes('*')
fmt.Println("res=", res)
}
方法和函式區別
- 呼叫方式不一樣
函式的呼叫方式: 函式名(實參串列)
方法的呼叫方式: 變數.方法名(實參串列) - 對于普通函式,接收者為值型別時,不能將指標型別的資料直接傳遞,反之亦然
- 對于方法(如 struct 的方法),接收者為值型別時,可以直接用指標型別的變數呼叫方法,反
過來同樣也可以
package main
import (
"fmt"
)
type Person struct {
Name string
}
//函式
//對于普通函式,接收者為值型別時,不能將指標型別的資料直接傳遞,反之亦然
func test01(p Person) {
fmt.Println(p.Name)
}
func test02(p *Person) {
fmt.Println(p.Name)
}
//對于方法(如struct的方法),
//接收者為值型別時,可以直接用指標型別的變數呼叫方法,反過來同樣也可以
func (p Person) test03() {
p.Name = "jack"
fmt.Println("test03() =", p.Name) // jack
}
func (p *Person) test04() {
p.Name = "mary"
fmt.Println("test03() =", p.Name) // mary
}
func main() {
p := Person{"tom"}
test01(p)
test02(&p)
p.test03()
fmt.Println("main() p.name=", p.Name) // tom
(&p).test03() // 從形式上是傳入地址,但是本質仍然是值拷貝
fmt.Println("main() p.name=", p.Name) // tom
(&p).test04()
fmt.Println("main() p.name=", p.Name) // mary
p.test04() // 等價 (&p).test04 , 從形式上是傳入值型別,但是本質仍然是地址拷貝
}
總結:
- 不管呼叫形式如何,真正決定是值拷貝還是地址拷貝,看這個方法是和哪個型別系結.
- 如果是和值型別,比如 (p Person) , 則是值拷貝, 如果和指標型別,比如是 (p *Person) 則
是地址拷貝,
面向物件編程應用實體
步驟
- 宣告(定義)結構體,確定結構體名
- 撰寫結構體的欄位
- 撰寫結構體的方法
學生案例:
- 撰寫一個 Student 結構體,包含 name、gender、age、id、score 欄位,分別為 string、string、int、
int、float64 型別, - 結構體中宣告一個 say 方法,回傳 string 型別,方法回傳資訊中包含所有欄位值,
- 在 main 方法中,創建 Student 結構體實體(變數),并訪問 say 方法,并將呼叫結果列印輸出,
- 走代碼
import (
"fmt"
)
/*
學生案例:
撰寫一個 Student 結構體,包含 name、gender、age、id、score 欄位,分別為 string、string、int、int、
float64 型別,
結構體中宣告一個 say 方法,回傳 string 型別,方法回傳資訊中包含所有欄位值,
在 main 方法中,創建 Student 結構體實體(變數),并訪問 say 方法,并將呼叫結果列印輸出,
*/
type Student struct {
name string
gender string
age int
id int
score float64
}
func (student *Student) say() string {
infoStr := fmt.Sprintf("student 的資訊 name=[%v] gender=[%v], age=[%v] id=[%v] score=[%v]",
student.name, student.gender, student.age, student.id, student.score)
return infoStr
}
func main() {
//測驗
//創建一個 Student 實體變數
var stu = Student{
name : "tom",
gender : "male",
age : 18,
id : 1000,
score : 99.98,
}
fmt.Println(stu.say())
}
盒子案例
- 編程創建一個 Box 結構體,在其中宣告三個欄位表示一個立方體的長、寬和高,長寬高要從終
端獲取 - 宣告一個方法獲取立方體的體積,
- 創建一個 Box 結構體變數,列印給定尺寸的立方體的體積
- 走代碼
景區門票案例
- 一個景區根據游人的年齡收取不同價格的門票,比如年齡大于 18,收費 20 元,其它情況門票免
費. - 請撰寫 Visitor 結構體,根據年齡段決定能夠購買的門票價格并輸出
- 代碼:
package main
import (
"fmt"
)
/*
學生案例:
撰寫一個Student結構體,包含name、gender、age、id、score欄位,分別為string、string、int、int、float64型別,
結構體中宣告一個say方法,回傳string型別,方法回傳資訊中包含所有欄位值,
在main方法中,創建Student結構體實體(變數),并訪問say方法,并將呼叫結果列印輸出,
*/
type Student struct {
name string
gender string
age int
id int
score float64
}
func (student *Student) say() string {
infoStr := fmt.Sprintf("student的資訊 name=[%v] gender=[%v], age=[%v] id=[%v] score=[%v]",
student.name, student.gender, student.age, student.id, student.score)
return infoStr
}
/*
1)編程創建一個Box結構體,在其中宣告三個欄位表示一個立方體的長、寬和高,長寬高要從終端獲取
2)宣告一個方法獲取立方體的體積,
3)創建一個Box結構體變數,列印給定尺寸的立方體的體積
*/
type Box struct {
len float64
width float64
height float64
}
//宣告一個方法獲取立方體的體積
func (box *Box) getVolumn() float64 {
return box.len * box.width * box.height
}
// 景區門票案例
// 一個景區根據游人的年齡收取不同價格的門票,比如年齡大于18,收費20元,其它情況門票免費.
// 請撰寫Visitor結構體,根據年齡段決定能夠購買的門票價格并輸出
type Visitor struct {
Name string
Age int
}
func (visitor *Visitor) showPrice() {
if visitor.Age >= 90 || visitor.Age <=8 {
fmt.Println("考慮到安全,就不要玩了")
return
}
if visitor.Age > 18 {
fmt.Printf("游客的名字為 %v 年齡為 %v 收費20元 \n", visitor.Name, visitor.Age)
} else {
fmt.Printf("游客的名字為 %v 年齡為 %v 免費 \n", visitor.Name, visitor.Age)
}
}
func main() {
//測驗
//創建一個Student實體變數
var stu = Student{
name : "tom",
gender : "male",
age : 18,
id : 1000,
score : 99.98,
}
fmt.Println(stu.say())
//測驗代碼
var box Box
box.len = 1.1
box.width = 2.0
box.height = 3.0
volumn := box.getVolumn()
fmt.Printf("體積為=%.2f", volumn)
//測驗
var v Visitor
for {
fmt.Println("請輸入你的名字")
fmt.Scanln(&v.Name)
if v.Name == "n" {
fmt.Println("退出程式....")
break
}
fmt.Println("請輸入你的年齡")
fmt.Scanln(&v.Age)
v.showPrice()
}
}
創建結構體變數時指定欄位值
說明
Golang 在創建結構體實體(變數)時,可以直接指定欄位的值
package main
import (
"fmt"
)
type Stu struct {
Name string
Age int
}
func main() {
//方式1
//在創建結構體變數時,就直接指定欄位的值
var stu1 = Stu{"小明", 19} // stu1---> 結構體資料空間
stu2 := Stu{"小明~", 20}
//在創建結構體變數時,把欄位名和欄位值寫在一起, 這種寫法,就不依賴欄位的定義順序.
var stu3 = Stu{
Name :"jack",
Age : 20,
}
stu4 := Stu{
Age : 30,
Name : "mary",
}
fmt.Println(stu1, stu2, stu3, stu4)
//方式2, 回傳結構體的指標型別(!!!)
var stu5 *Stu = &Stu{"小王", 29} // stu5--> 地址 ---》 結構體資料[xxxx,xxx]
stu6 := &Stu{"小王~", 39}
//在創建結構體指標變數時,把欄位名和欄位值寫在一起, 這種寫法,就不依賴欄位的定義順序.
var stu7 = &Stu{
Name : "小李",
Age :49,
}
stu8 := &Stu{
Age :59,
Name : "小李~",
}
fmt.Println(*stu5, *stu6, *stu7, *stu8) //
}
工廠模式
Golang 的結構體沒有建構式,通常可以使用工廠模式來解決這個問題,
看一個需求
一個結構體的宣告是這樣的:
package model
type Student struct {
Name string...
}
因為這里的 Student 的首字母 S 是大寫的,如果我們想在其它包創建 Student 的實體(比如 main 包),
引入 model 包后,就可以直接創建 Student 結構體的變數(實體), 但是問題來了 , 如果首字母是小寫的 ,
如 比如 是 是 type student struct {....} 就不不行了,怎么辦---> 工廠模式來解決.
工廠模式來解決問題
使用工廠模式實作跨包創建結構體實體(變數)的案例:
如果 model 包的 結構體變數首字母大寫,引入后,直接使用, 沒有問題
如果 model 包的 結構體變數首字母小寫,引入后,不能直接使用, 可以 工廠模式解決, 看老師演
示, 代碼:
student.go
package model
//定義一個結構體
type student struct{
Name string
score float64
}
//因為student結構體首字母是小寫,因此是只能在model使用
//我們通過工廠模式來解決
func NewStudent(n string, s float64) *student {
return &student{
Name : n,
score : s,
}
}
//如果score欄位首字母小寫,則,在其它包不可以直接方法,我們可以提供一個方法
func (s *student) GetScore() float64{
return s.score //ok
}
main.go
package main
import (
"fmt"
"go_code/chapter10/factory/model"
)
func main() {
//創建要給Student實體
// var stu = model.Student{
// Name :"tom",
// Score : 78.9,
// }
//定student結構體是首字母小寫,我們可以通過工廠模式來解決
var stu = model.NewStudent("tom~", 98.8)
fmt.Println(*stu) //&{....}
fmt.Println("name=", stu.Name, " score=", stu.GetScore())
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/67385.html
標籤:Go
上一篇:GO基礎之介面
下一篇:GO基礎之例外處理
