结构体
-
一个程序就是一个世界,有很多对象(变量)
思路
1、用变量解决
2、用数组解决
使用变量和数组来解决不利于数据的维护。所以引出用结构体来抽象猫的特性。
Golang语言面向对象编程说明
1、golang也支持面向对象(OOP),但是和传统的面向对象编程有区别。并不是纯粹的面向对象
。应该说。Golang支持面向对象特性。
2、Golang没有类(class
),Go语言的结构体(struct
)和其它语方的类(class
)有同等的地位,你可以理解Golang是基于struct
来实现OOP特性。
3、Golang面向对象非常简洁,去掉了传统OOP
语言的继承,方法重载,构造函数,析构函数、隐藏的this指针
。
4、Golang仍然有面向对象编程的继承,封装和多态特性。只是实现的方式和其它不一样。比如继承:Golang没有extends
关键字,继承是通过匿名字段
来实现的。
5、Golang面向对象(OOP)很优雅。OOP本身就是语方类型(type stystem)的一部分。通过接口(interface
)关联。耦合性低
,Golang中面向接口编程
是非常重要的特性。
对上图的说明
1、将一类事物的特性提取出来(比如猫类),形成一个新的数据类型(结构体)。
2、通过结构体,可以创建多个变量(实例)
3、事物可以猫类,人类,或某个工具类。
- 快速入门解决(面向对象解决养猫的问题)
//定义结构体
type Cat struct {
Name string //""
Age int // 0
Color string // ""
Hobby string
}
func main() {
//创建一个cat的变量
var cat1 Cat
fmt.Println(cat1)
cat1.Name = "小白"
cat1.Age = 3
cat1.Color = "白色"
cat1.Hobby = "吃鱼 <*(((()->"
fmt.Println(cat1)
fmt.Println("猫名:", cat1.Name)
fmt.Println("猫年龄:", cat1.Age)
fmt.Println("猫颜色:", cat1.Color)
fmt.Println("猫喜欢:", cat1.Hobby)
}
通过上面案例可以看出
1、结构体是自定义的数据类型
,代表一类事物
2、结构体变量(实例)是具体的,实际的
,代表一个具体变量。
结构体变量在内存中的布局。
结构体声明
type 结构体名称 struct {
字段 字段类型
Age int // 0
Color string // ""
Hobby string
//首字母大写是公有的,可以被其它包使用
}
字段/属性注意及说明
1、字段声明语法同变量,字段名 字段类型
2、字段的类型可为:基本类型、数组或引用类型
3、在创建一个结构体充数量后。如果没有赋值,都对应一个零值
布尔类型是
false
,数值是0
,字符串是""
。
数组类型
的默认值和它的元素类型相关,比如scope [3]int 默认值 是[0,0,0]
指针,slice,和map
的零值是nil
,即还没有分配内存空间
//定义结构体
type Person struct {
Name string //""
Age int // 0
Scores [5]float64
Ptr *int //指针
Slice []int //切片
Map1 map[string]string //map
}
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")
}
//先make再赋值
p1.Slice = make([]int, 10)
p1.Slice[0] = 100
fmt.Println(p1)
p1.Map1 = make(map[string]string)
p1.Map1["key1"] = "tom~~"
fmt.Println(p1)
}
4、不同结构体的变量的字段是独立的,互不影响。
//定义结构体
type Monster struct {
Name string
Age int
}
func main() {
//定义结构体变量
var monster1 Monster
monster1.Name = "牛魔王"
monster1.Age = 500
monster2 := monster1 //默认结构体是值类型,默认为值拷贝
monster2.Name = "青牛精"
fmt.Println("monster1=", monster1)
fmt.Println("monster2=", monster2)
}
上面代码的内存示意图
创建结构体变量和访问结构体字段 4种方式
1、方式1- 直接声明
//案例:
var person Person
2、方式2- { }
//定义结构体
type Monster struct {
Name string
Age int
}
func main() {
//定义结构体变量
p2 := Monster{"mary", 20}
//p2.Name = "tom"
fmt.Println(p2)
}
3、方式3 - 通过new
//定义结构体
type Person struct {
Name string
Age int
}
func main() {
//方式3
var p3 *Person = new(Person)
//因为祝p3是一个指针,标准方式是
//(*p3).Name = "smith" 也可以这样写 p3.Name = "smith"
//为了方便使用,会在底层做了处理
//会给 p3加上 取值运算 (*p3).Name = "smith"
(*p3).Name = "smith"
p3.Name = "john"
(*p3).Age = 30
p3.Age = 99
fmt.Println(*p3)
}
4、方式4 - &{ }
//定义结构体
type Person struct {
Name string
Age int
}
func main() {
//方式3
var p3 *Person = new(Person)
//因为祝p3是一个指针,标准方式是
//(*p3).Name = "smith" 也可以这样写 p3.Name = "smith"
//为了方便使用,会在底层做了处理
//会给 p3加上 取值运算 (*p3).Name = "smith"
(*p3).Name = "smith"
p3.Name = "john"
(*p3).Age = 30
p3.Age = 99
fmt.Println(*p3)
//方式4 - &name{}
var person *Person = &Person{}
//创建的时候赋值
//var person *Person = &Person{"mary",60}
//因为person是一个指针,因此标准的用法是
//(*person).Name = "scott"
//但是为了简化,go的设计者为了程序员方便,也可以直接这样用
//person.Name = "scott"
(*person).Name = "scott"
person.Name = "scott~~~"
(*person).Age = 88
person.Age = 111
fmt.Println(*person)
}
说明:
1、第3种和第4种方式返回的都是
结构体的指针
2、结构体指针访问字段
的标准方式是结构体指针.字段名
,比如:
(*person).Name = "tom"
3、但是go做了简化,它也支持结构体名.字段名
,因为go在编译器层做了转化处理。
struct类型的内存分配机制
- 变量总是存在内存中的。
结构体的变量在内存中是如何存在的,看下面图示
//定义结构体
type Person struct {
Name string
Age int
}
func main() {
var p1 Person
p1.Age = 10
p1.Name = "小明"
var p2 *Person = &p1
fmt.Println((*p2).Age)
fmt.Println(p2.Age)
p2.Name = "tom~"
fmt.Printf("p2.Name = %v p1.Name = %v \n", p2.Name, p1.Name)
fmt.Printf("p2.Name = %v p1.Name = %v \n", (*p2).Name, p1.Name)
fmt.Printf("p1的地址:%p\n", &p1)
fmt.Printf("p2的值为:%p\n", p2)
fmt.Printf("p2的地址:%p\n", &p2)
}
注意
fmt.Println(*p2.Age)
写法会报错。原因是:.
的优先级比*
高,
所以用()
括起来。()
优先级大于.