Go语言学习之面向对象(1)结构体
1.结构体
1.1问题:
乔老汉生了两个美艳绝伦的女儿,大乔和小乔,
大乔:年龄 :20,老公:孙策,爱好:吃鱼
小乔:年龄:18,老公:周瑜,爱好:吃鸡
1.2使用现有技术解决:
var name1 string="大乔"
var age1 int =20
var laoGong1 string="孙策"
varlover1 string="吃鱼"
var name2 string="小乔"
var age2 int =18
var laoGong2 string="周瑜"
varlover2 string="吃鸡"
1.3使用数组解决:
var name [2]string=[...]string{"大乔",“小乔”}
var age [2]int=[...]int{20,18}
var laogong [2]string=[...]{"孙策",“周瑜”}
1.4现有技术的缺点·分析
1》使用变量或者数组来解决大小乔问题,不利于数据的管理和维护。因为名字,年龄,老公都是属于一个人,但是这里是分开保存
2》如果我们希望对一个人的属性(姓名,年龄,老公)进行操作(绑定方法),不好处理
3》这里引出结构体技术
一个程序就是一个世界,里面有很多对象(变量,函数)
1.5GO语言面向对象编程说明:
1》go支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说go支持面向对象编程的特性是比较准确的
2》GO没有类(classs),GO语言的结构体(struct)和其他编程语言的类(class),有同等的地位,你可以理解GO是基于struct来实现OOP特性
3》go面向对象编程非常简洁,去掉传统OOP语言的继承方法重载,构造函数和析构函数,隐藏的this指针等等
4》GO仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样,比如继承:GO没有extends关键字,继承是通过匿名字段来实现
5》go面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。GO中面向接口编程是非常重要的特性
1.6结构体与结构体变量(实例/对象)的关系
把所有人的特征提取出来===》MM结构体=====》 变量1(实例)
1.字段/属性 变量2(实例)
name,age,laogong
2.行为(方法)
对上面内容说明:
1》将一类事物的特性取出来(比如MM类),形成一个新的数据类型,就是结构体
2》通过这个结构体,我们可以创建多个变量(实例/对象)
3》事物可以MM类,也可以狗类或是某个工具类
从MM结构体到变量,就是创建一个MM结构体变量,也可以说是定义一个MM结构体变量。当然上面的MM可以是很多种事物
1.7快速入门–面向对象方式(struct)解决MM的问题
package main
import "fmt"
type MM struct {
name string
age int
laogong string
}
func main() {
//出那个键一个MM变量
var mm1 MM
mm1.name="大乔"
mm1.age=20
mm1.laogong="孙策"
fmt.Println(mm1)
fmt.Println("美女的信息如下:")
fmt.Println("name=",mm1.name)
fmt.Println("age=",mm1.age)
fmt.Println("laogong=",mm1.laogong)
}
1.8结构体和结构体变量(实例)的区别和联系
通过上面案例和讲解我们可以看出
1》结构体是自定义数据类型,代表一类事物
2》结构体变量(实例)是具体的,实际的,代表一个具体变量
1.9结构体变量(实例)在内存的布局
1.10如何声明结构体
基本语法:
type 结构体名称 struct{
field1 type
field2 type
}
举例:
type Student struct{
Name string//字段
Class int//字段
Score float64
}
11.字段/属性
基本介绍:
1》从概念叫法上看:结构体字段=属性=filed
2》字段是结构体的一个组成部分,一般是基本数据类型,数组,也可以是引用数据类型,比如:我们前面定义的MM结构体 中的 name string 就是属性
注意事项:
1》字段声明语法同变量:示例:字段名 字段类型
2》字段的类型可以为:基本类型,数组或引用类型
3》在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样
布尔类型:false,数值为0,字符串“”
数组类型的默认值和它的元素类型相关,比如:
score[3]int 则为[0,0,0]
&&&&指针,slice ,和map的零值都是nil,即还没有分配空间
package main
import "fmt"
type MM struct {
name string
age int
high float64
sweight float64
ptr *int//指针
slice []int//切片
map1 map[string]string //map
}
func main() {
//定义结构体变量
var xishi MM
fmt.Println(xishi)
if xishi.ptr==nil{
fmt.Println("*ptr is nil")
}
if xishi.slice==nil{
fmt.Println("slice is nil")
}
if xishi.map1==nil{
fmt.Println("map1 is nil")
}
//使用slice 一定要先make
xishi.slice=make([]int,2)
xishi.slice[0]=999
//使用 map一定要先map
xishi.map1=make(map[string]string)
xishi.map1["love"]="范蠡"
fmt.Println(xishi)
}
4》不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个,结构体是值类型
package main
import "fmt"
type Cat struct {
name string
age int
color string
}
func main() {
var lanMao Cat
lanMao.name="咪咪"
lanMao.age=3
lanMao.color="blue"
fmt.Println(lanMao)//{咪咪 3 blue}
tomMao := lanMao
tomMao.name="tommaomao"//结构体是值类型,默认值拷贝
tomMao.color="黑色"
fmt.Println(lanMao)//{咪咪 3 blue}
fmt.Println(tomMao)//{tommaomao 3 黑色}
}
12.创建结构体变量和访问结构体字段
方式1》直接声明
var lanmao Cat
方式2》{}
var lanmao Cat =Cat{}
方式3》&
var lanmao *Cat=new (Cat)
var lanmao *Cat=new (Cat)
//因为lanmao 是一个指针,所以标准的给字段赋值方式为
(*lanmao).Name="mimi"
//当然也可以写成
lanmao.Name="mimi"
//因为 go设计者为了使用方便,底层对lanmao.Name="mimi"进行处理
//会给lanmao加上取值运算 (*lanmao).Name="mimi"
方式4》{}
var lanmao *Cat=&Cat{}
var tom *Cat=&Cat{}
//因为 tom是一个指针,因此标准的访问字段的方法
(*tom).name="汤姆"
fmt.Println(tom)
//go的设计,为了使用方便,也可以 tom.Name="汤姆“
tom.name="汤姆弟弟"
fmt.Println(tom)
说明:
(1)第三,四种方式返回的是 结构体指针
(2)结构体指针访问字段的标准方式应该是:(*结构体指针)字段名,比如
(*tom).name=“汤姆”
(3)但go 做了简化操作,也支持 结构体指针.字段名
比如:tom.name=“汤姆”
但在底层会将这种形式转换成(2)
13.struct类型的内存分配机制
package main
import "fmt"
type Dog struct {
name string
age int
}
func main() {
var dog1 Dog
dog1.name="金毛"
dog1.age=3
var dog2 Dog=dog1
dog2.name="马犬"
fmt.Println(dog2.age)
fmt.Printf("p2.name=%v p1.name=%v",dog2.name,dog1.name)
}
/*
结果:
3
p2.name=马犬 p1.name=金毛
*/
结构体在内存中的示意图:
暂时略
看下面代码,分析原因:
var dog1 Dog
dog1.name="金毛"
dog1.age=3
var dog2 *Dog=&dog1//这个是关键,把地址一切都给了dog02
fmt.Println((*dog2).age)
fmt.Println(dog2.age)
dog2.name="马犬"
fmt.Printf("dog2.name=%v dog.name=%v\n",dog2.name,dog1.name)
fmt.Printf("dog2.name=%v dog1.name=%v\n",(*dog2).name,dog1.name)
fmt.Printf("dog1地址:%p\n",&dog1)
fmt.Printf("dog2地址:%p,dog2=%p\n",&dog2,dog2)
结果:
3
3
dog2.name=马犬dog1.name=马犬
dog2.name=马犬 dog1.name=马犬
dog1地址:0xc0000044a0
dog2地址:0xc000006028,dog2=0xc0000044a0
记住:(*dog2).name 不能写成 *dog.name,会报错
原因: . 的运行优先级高于 *,必须加上()
14.结构体使用注意事项和细节
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\nr1.leftUp.y地址=%p,\nr1.rigntDown.x 地址=%p,\nr1.rightDown.y地址=%p\n",
&r1.leftUp.x,&r1.leftUp.y,
&r1.rightDown.x,&r1.rightDown.y)
/*
r1.leftUp.x 地址=0xc00007a040
r1.leftUp.y地址=0xc00007a048,
r1.rigntDown.x 地址=0xc00007a050,
r1.rightDown.y地址=0xc00007a058
*/
//r2 有两个*point类型,这两个Point类型的地址本身也是相连的
//但是他们所指向的地址不一定连续
r2:=rect2{&point{10,20},&point{30,40}}
//打印地址
//地址本身是连续的
fmt.Printf("r2.leftUp本身地址为 %p \n r2.rightDown 本身地址为 %p\n",&r2.leftUp,&r2.rightDown)
//指向地址是不连续的
fmt.Printf("r2.leftUp的指向地址为%p \n r2.rightDown 本身地址为 %p\n",r2.leftUp,r2.rightDown)
}
2》结构体是用户单独定义的类型,和其他类型进行转换石需要有完全相同的字段(名字,个数和类型)
package main
import "fmt"
type A struct {
name string
}
type B struct {
name string
}
func main() {
var a A
var b B
a=A(b)
fmt.Println(a,b)
}
3》结构体进行type重新定义(相当于取别名),GO认为是新的数据类型
但是相互间可以强转
package main
import "fmt"
type Student struct {
Name string
age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
//stu2=stu1 直接转会报错,两种数据类型
stu2=Stu(stu1)
fmt.Println(stu1,stu2)
}
4》struct 的每个字段上,可以写上一个tag,该tag可以通过反射机制获取
常见的使用场景就是序列化和反序列化
主要用于客户端和服务端参数传递的解析
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"` //'jsonx:"name"' 就是struct的tag
Age int `jsonx:"age"`//反引号
}
func main() {
//创建一个 Monster变量
monster:=Monster{"白骨精",100}
//将monster变量序列化为json格式字串
//json.Marshal函数中使用反射机制,这个后面会有专门讲解
jsonstr,err:=json.Marshal(monster)
if err!=nil{
fmt.Println("json处理错误",err)
}
fmt.Println("jsonstr",string(jsonstr))
}