golang学习笔记011--面向对象编程1

1.go语言面向对象说明

● golang也支持面向对象编程(OOP),但是和传统的面向对象有区别,并不是纯粹的面向对象.所以go支持面向对象编程特性是比较准确的
● golang没有类(Class),go的结构体(struct)和其它语言的类(Class)有同等地位
● golang面向对象编程非常简洁,去掉了传统的OOP语言的方法重载,构造函数和析构函数,隐藏的this指针等
● golang仍有面向对象编程的继承,封装和多态的特性,只是实现方式和其它OOP语言不一样,比如继承:golang没有extends关键字,继承是通过匿名字段来实现
● golang面向对象(OOP)很优雅,OOP本身就是语言类型系统的一部分,通过接口(interface)关联,耦合性低,也非常灵活

2.结构体

2.1 结构体与结构体变量的关系图

在这里插入图片描述

2.2 快速入门

package main
import "fmt"

type Cat struct {
	Name string
	Age int
	Color string
	Hobby string
}


func main(){
	//创建一个Cat变量
	var Cat1 Cat // var a int
	Cat1.Name = "小白"
	Cat1.Age = 10
	Cat1.Color = "白色"
	Cat1.Hobby = "吃鱼"

	fmt.Println(Cat1)//{小白 10 白色 吃鱼}
	fmt.Println("name:",Cat1.Name)//name: 小白
}

在这里插入图片描述

2.3 如何声明结构体

● 基本语法:
	type 结构体名称 struct {
		field1 type
		field2 type
	}
● 基本介绍
  ○ 结构体字段=属性=field
  ○ 字段是结构体的一个组成部分,一般是基本数据类型,数组,也可是引用类型
● 注意事项
  ○ 字段声明语法同变量
  ○ 字段的类型可以为:基本类型,数组或引用类型
  ○ 不同结构体变量的字段是独立,互不影响的,一个结构体的字段更改,不会影响到另外一个,结构体是值传递
  ○ 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值)
    ■ 布尔是false,数值是0,字符串是""
    ■ 数组类型的默认值和它的元素类型相关
    ■ 指针,slice,map的零值都是nil,既没有分配空间
	package main
	import "fmt"
	
	type Cat struct {
		Name string
		Age int
		Color string
		Hobby string
		slice []int
		map1 map[string]string
	}
	
	
	func main(){
		//创建一个Cat变量
		var Cat1 Cat // var a int
		Cat1.Name = "小白"
		Cat1.Age = 10
		Cat1.Color = "白色"
		Cat1.Hobby = "吃鱼"
	    
		Cat1.slice = make([]int,10)
		Cat1.slice[0]=100
	
		Cat1.map1 = make(map[string]string)
		Cat1.map1["no1"] = "tom"
	
	}

2.4 创建结构体方式

方式一:var Cat1 Cat

上面的方式
var Cat1 Cat

方式二:varCat2 Cat = Cat{}

varCat2 Cat = Cat{}
Cat2.Name = "小花"
fmt.Println(Cat2)//{小花 0   [] map[]}
或者varCat2 Cat = Cat{"小花",20等}

方式三:varCat3 *Cat = new(Cat)

varCat3 *Cat = new(Cat)
//标准写法
(*Cat3).Name = "小花"
//go的设计者为了方便,底层对Cat3.Name = "小花~~"进行了处理
Cat3.Name = "小花~~"
fmt.Println(Cat3)//&{小花~~ 0   [] map[]}

方式四:varCat4 *Cat = &Cat{}

varCat4 *Cat = &Cat{}
(*Cat4).Name = "小黑"
Cat4.Name = "小黑~~"
fmt.Println(Cat4)//&{小黑~~ 0   [] map[]}

说明:

○ 第三种和第四种返回的是 结构体指针
○ 结构体指针访问字段的标准格式为:(*Cat4).Name = “小黑”,但是go做了简化,也支持Cat4.Name = “小黑~~”,底层进行了转换
○ fmt.Println(Cat4.Name)这样写是错误的,因为.优先级高于

2.5 结构体使用注意事项

● 结构体的所有字段在内存中都是连续的
● 结构体使用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字,个数和类型),需要强转
在这里插入图片描述
● 结构体进行type重新定义后(相当于取别名),golang认为是新的数据类型,但是可以强转
在这里插入图片描述
● struct的每个字段上,可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化
在这里插入图片描述

3.方法

golang中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型都可以有方法,而不仅仅是struct

3.1 快速入门

	package main
	
	import "fmt"
	
	type Person struct{
		Name string
		Age int
	}
	type Dog struct{
		Name string
		Age int
	}
	
	func (person Person) test(){
		fmt.Println(person.Name)
	}
	
	func main(){
	
		var p1 Person
		p1.Name = "tom"
		p1.Age = 20
		p1.test()//tom 正确
	    test()//错误
	
		var D1 Dog
		D1.Name = "haha"
		//如果别的类型调用该方法会报错
		D1.test()//D1.test undefined (type Dog has no field or method test) 错误
	
	}

说明:
● test方法和Person类型绑定
● test方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调用
● func (person Person) test() 。。。。 person表示哪个Person变量调用,person就是这个Person变量的副本

3.2 方法的声明/定义

func (recevier type) methodName (参数列表) (返回值列表){
    方法体
    return 返回值
}

● 参数列表:表示方法输入
● recevier type:表示这个方法和type这个类型进行绑定,或着说该方法作用域type类型上
● recevier type:type可以是结构体,也可以是其他的自定义类型
● recevier:就是type类型的一个变量
● 返回值列表:表示返回的值,可以多个
● return语句不是必须的

3.3 方法的注意事项

● 结构体类型是值类型,在方法调用中,遵守值类型的传递机制
● 如果希望在方法中修改结构体变量的值,可以通过结构体指针的方式来处理

	func (person *Person) update(str string){
		//(*person).Name = str //标准写法
		person.Name = str //也可以这样写,编译器底层做了优化
	}
	
	func main(){
	
		var p1 Person
		p1.Name = "tom"
		p1.Age = 20
		p1.test()//tom
	
		//标准写法
		//(&p1).update("tom~~~")	//标准写法
		p1.update("tom~~")也可以这样写,编译器底层做了优化
	
		fmt.Println(p1)

● golang中的方法作用在指定的数据类型上的,因此定义类型都可以有方法,而不仅仅是struct,比如int ,float32等都可以有方法
● 比如int 要type myInt int,不然报错

	package main
	
	import "fmt"
	
	func (i int) test(){
		fmt.Println(i)
	}
	
	func main(){
	
		i := 10
		i.test()//报错 i.test undefined (type int has no field or method test)
	}
	package main
	
	import "fmt"
	
	type myInt int
	
	func (i myInt) test(){
		fmt.Println(i)
	}
	
	func main(){
	
		var i myInt =10
		i.test()//10
	}

● 方法的访问的范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写可以在本包和其他包访问
● 如果一个类型实现了String()方法,那么fmt.Prinltn默认会调用这个变量的String()进行输出

4.方法和函数的比较

● 调用方式不一样
○ 函数的调用方式:函数名(实参列表)
○ 方法的调用方式:变量名.方法名(实参列表)
● 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然

	package main
	
	import "fmt"
	
	type Person struct{
		Name string
	}
	
	//函数
	func test01(p Person){
		fmt.Println(p.Name)
	}
	
	func main(){
	
		p := Person{"tom"}
		test01(p)//正确
		test01(&p)//错误 cannot use &p (type *Person) as type Person in argument to test01
	
	}

● 对于方法(如struct的方法),接收者为值类型,可以直接用指针类型的变量调用方法,反过来也一样

	package main
	
	import "fmt"
	
	type Person struct{
		Name string
	}
	
	//函数
	func test01(p Person){
		fmt.Println(p.Name)
	}
	
	//方法
	func (p Person) test02(){
		p.Name = "jack"
	}
	
	func (p *Person) test03(){
		p.Name = "jack"
	}
	
	func main(){
	
		p := Person{"tom"}
		//函数
		test01(p)//正确  tom
		//test01(&p)//错误 cannot use &p (type *Person) as type Person in argument to test01
		//方法
		p.test02()
		fmt.Println(p)//{tom} 正确
		//函数
		// p.test02()//正确
		// (&p).test02()//正确 因为方法是值传递,所以编译器底层会优化为p.test02(),但是不会改变Name的值
		// fmt.Println(p)//{tom} 正确
	
		 //(&p).test03()//正确
		 p.test03()//正确 因为方法是引用传递,所以编译器底层会优化为(&p).test03(),Name的值会改变
		 fmt.Println(p)//{jack}
	
	
	}

总结:
● 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看的是这个方法和哪个类型绑定
● 比如 p Person 是值拷贝 , p *Person地址拷贝

5.面向对象编程应用实例

步骤:
(1)声明结构体,确定结构体名
(2)编写结构体的字段
(3)编写结构体的方法

6.工厂模式

golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题

6.1 需求

一个结构体:
package model
type Student struct{
Name string.....
}
因为这里的Student的首字母是大写的,如果我们想在其它包创建Student的实例,比如main包,引入model包后,就可以直接创建Student结构体的变量
问题:如果Student首字母是小写的,就不行了,怎么办。。。。。使用工厂模式来解决

6.2 工厂模式解决问题

使用工厂模式实现跨包创建结构体实例案例

(1)结构体名为大写,在其它包使用(完全正确)

如果model包的结构体变量首字母大写,引入后,直接使用,没有问题
● 在model定义结构体
在这里插入图片描述
● 在main中创建实例,Student首字母是大写,完全正确
在这里插入图片描述
● 结果:{tom 88.8}

(2)结构体名为小写,在其他包使用(报错)

在这里插入图片描述
如果model包结构体首字母为小写,引入后不能直接使用,可以使用工厂模式
修改student.go代码

	package model
	
	//定义结构体
	type student struct{
		Name string
		Score float64
	}
	
	//因为student首字母为小写,只能在该包下使用
	//通过工厂模式解决
	
	func NewStudent(n string, s float64) *student{
		return &student{
			Name : n,
			Score : s,
		}
	}

main.go 代码

	package main
	
	import (
		"fmt"
		"go_code/Project01/factory/model"
	)
	
	func main(){
		//创建Student实例
		// var stu = model.Student{
		// 	Name: "tom",
		// 	Score : 88.8,
		// }
	
		//student首字母为小写,调用工厂模式
		var stu = model.NewStudent("tom",90.5)
		fmt.Println(stu)//&{tom 90.5}   返回的是一个地址
		fmt.Println(*stu)//{tom 90.5}   取值
	}

(3)结构体名大写,字段小写(报错)

如果Name首字母变成小写,则报错
在这里插入图片描述
修改student.go代码

	package model
	
	//定义结构体
	type student struct{
		name string
		Score float64
	}
	
	//因为student首字母为小写,只能在该包下使用
	//通过工厂模式解决
	
	func NewStudent(n string, s float64) *student{
		return &student{
			name : n,
			Score : s,
		}
	}
	
	
	//name首字母为小写
	func (s *student) GetName() string{
		return s.name
	}

main.go

	package main
	
	import (
		"fmt"
		"go_code/Project01/factory/model"
	)
	
	func main(){
		//创建Student实例
		// var stu = model.Student{
		// 	Name: "tom",
		// 	Score : 88.8,
		// }
	
		//student首字母为小写,调用工厂模式
		var stu = model.NewStudent("tom",90.5)
		
		fmt.Println("name=",stu.GetName(),"Score=",stu.Score)//name= tom Score= 90.5
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值