Go语言学习之面向对象(1)结构体

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))
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值