1) Golang 也支持面向对象编程(OOP),可是和传统的面向对象编程有区别,并非纯粹的面向对
象语言。因此咱们说 Golang 支持面向对象编程特性是比较准确的。
2) Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可
以理解 Golang 是基于 struct 来实现 OOP 特性的。
3) Golang 面向对象编程很是简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函
数、隐藏的 this 指针等等程序员
4) Golang 仍然有面向对象编程的 继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不
同样,好比继承 :Golang 没有 extends 关键字,继承是经过匿名字段来实现。
5) Golang 面向对象(OOP)很优雅,OOP 自己就是语言类型系统(type system)的一部分,经过接口
(interface)关联,耦合性低,也很是灵活。也就是说在 Golang 中面
向接口编程是很是重要的特性。golang
1) 将一类事物的特性提取出来(好比猫类), 造成一个新的数据类型, 就是一个结构体。
2) 经过这个结构体,咱们能够建立多个变量(实例/对象)
3) 事物能够猫类,也能够是 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) }
经过上面的案例和讲解咱们能够看出:
1) 结构体是自定义的数据类型,表明一类事物.
2) 结构体变量(实例)是具体的,实际的,表明一个具体变量
结构体变量(实例)在内存的布局(重要!)json
基本语法数组
type 结构体名称 struct { field1 type field2 type } //举例: type Student struct { Name string //字段 Age int //字段 Score float32 }
基本介绍
1) 从概念或叫法上看: 结构体字段 = 属性 = field (即授课中,统一叫字段)
2) 字段是结构体的一个组成部分,通常是 基本数据类型、 数组,也但是 引用类型。好比咱们前面定
义猫结构体 的 Name string 就是属性安全
注意事项和细节说明
1) 字段声明语法同变量,示例:字段名 字段类型
2) 字段的类型能够为:基本类型、数组或引用类型
3) 在建立一个结构体变量后,若是没有给字段赋值,都对应一个零值(默认值),规则同前面讲的
同样:
布尔类型是 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} }
案例演示: var person Person
前面咱们已经说了。函数
案例演示: var person Person = Person{}工具
案例: var person *Person = new (Person)布局
案例: var person *Person = &Person{}
1) 第 3 种和第 4 种方式返回的是 结构体指针。
2) 结构体指针访问字段的标准方式应该是:(结构体指针).字段名 ,好比 (person).Name = "tom"
3) 但 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) }
略
1) 结构体的全部字段在 内存中是连续的
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) }
2) 结构体是用户单独定义的类型,和其它类型进行转换时须要有彻底相同的字段(名字、个数和类
型)
3) 结构体进行 type 从新定义(至关于取别名),Golang 认为是新的数据类型,可是相互间能够强转
4) 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) }
** 对上面的语法的说明**
1) func (aA) test() {} 表示 A 结构体有一方法,方法名为 test
2) (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 , 从形式上是传入值类型,可是本质仍然是地址拷贝 }
对上面的总结
1) test 方法和 Person 类型绑定
2) test 方法只能经过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调
用
3) func (p Person) test() {}... p 表示哪一个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非
常类似。
4) p 这个名字,有程序员指定,不是固定, 好比修改为 person 也是能够
1) 给 Person 结构体添加 speak 方法,输出 xxx 是一个好人
2) 给 Person 结构体添加 jisuan 方法,能够计算从 1+..+1000 的结果, 说明方法体内能够函数同样,
进行各类运算
3) 给 Person 结构体 jisuan2 方法,该方法能够接收一个数 n,计算从 1+..+n 的结果
4) 给 Person 结构体添加 getSum 方法,能够计算两个数的和,并返回结果
5) 方法的调用
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) }
方法的调用和传参机制和函数基本同样,不同的地方是方法调用时,会将调用方法的变量,当作
实参也传递给方法。下面咱们举例说明。
1) 在经过一个变量去调用方法时,其调用机制和函数同样
2) 不同的地方时,变量调用方法时,该变量自己也会做为一个参数传递到方法(若是变量是值类
型,则进行值拷贝,若是变量是引用类型,则进行地质拷贝)
请编写一个程序,要求以下:
1) 声明一个结构体 Circle, 字段为 radius
2) 声明一个方法 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 返回值 }
1) 参数列表:表示方法输入
2) recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法做用于 type 类型
3) receiver type : type 能够是结构体,也能够其它的自定义类型
4) receiver : 就是 type 类型的一个变量(实例),好比 :Person 结构体 的一个变量(实例)
5) 返回值列表:表示返回的值,能够多个
6) 方法主体:表示为了 实现某一功能代码块
7) return 语句不是必须的。
1) 结构体类型是值类型,在方法调用中,遵照值类型的传递机制,是值拷贝传递方式
2) 如程序员但愿在方法中,修改结构体变量的值,能够经过结构体指针的方式来处理
3) 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) }
1) 调用方式不同
函数的调用方式: 函数名(实参列表)
方法的调用方式: 变量.方法名(实参列表)
2) 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
3) 对于方法(如 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 , 从形式上是传入值类型,可是本质仍然是地址拷贝 }
总结:
1) 无论调用形式如何,真正决定是值拷贝仍是地址拷贝,看这个方法是和哪一个类型绑定.
2) 若是是和值类型,好比 (p Person) , 则是值拷贝, 若是和指针类型,好比是 (p *Person) 则
是地址拷贝。
1) 声明(定义)结构体,肯定结构体名
2) 编写结构体的字段
3) 编写结构体的方法
1) 编写一个 Student 结构体,包含 name、gender、age、id、score 字段,分别为 string、string、int、
int、float64 类型。
2) 结构体中声明一个 say 方法,返回 string 类型,方法返回信息中包含全部字段值。
3) 在 main 方法中,建立 Student 结构体实例(变量),并访问 say 方法,并将调用结果打印输出。
4) 走代码
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()) }
1) 编程建立一个 Box 结构体,在其中声明三个字段表示一个立方体的长、宽和高,长宽高要从终
端获取
2) 声明一个方法获取立方体的体积。
3) 建立一个 Box 结构体变量,打印给定尺寸的立方体的体积
4) 走代码
1) 一个景区根据游人的年龄收取不一样价格的门票,好比年龄大于 18,收费 20 元,其它状况门票免
费.
2) 请编写 Visitor 结构体,根据年龄段决定可以购买的门票价格并输出
3) 代码:
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()) }