Go面向对象-行为的定义和实现

目录

 

一、行为的定义和实现

1、Go语言是面向对象语言吗?

2、封装数据和行为

a、结构体定义

3、实例的创建和初始化

4、行为方法的创建和定义

a、方案1,实例对应方法被调用时,实例的成员会进行值复制

b、方案2,采用指针的方案,实例成员不会进行值复制(推荐)

c、对比对两种行为方法的不同


一、行为的定义和实现

1、Go语言是面向对象语言吗?

官方给的解释是:Yes or no (即是也不是),因为面向对象中有个很重要的概念是继承,但是 Go 语言中是不支持继承的,Go 的接口采用了 Duck type 的方式,这和其它主流编程语言是完全不一样的,用起来也更加方便。

2、封装数据和行为

a、结构体定义

//和 c 一样,用关键字 struct 定义结构体,然后把数据成员定义在花括号中
type Employee struct {
	Id string
	Name string
	Age int
}

3、实例的创建和初始化

package encapsulation

import "testing"

//和 c 一样,用关键字 struct 定义结构体,然后把数据成员定义在花括号中
type Employee struct {
	Id int
	Name string
	Age int
}

//创建一个 Employee 对象
func TestCreateEmployeeObj(t *testing.T)  {
	//方案1
	emp1 := Employee {1,  "Bob", 24}

	//方案2:方案2和方案1的差别就是指定每个 filed 的名字在给值
	emp2 := Employee{Id: 2, Name: "Mike", Age: 25}

	//方案3,用 new 关键字创建一个指向实例的指针
	emp3 := new (Employee)	//注意:方案3返回的是一个指针,相当于 emp3 := &Employee{}
	//然后给每个成员赋值
	emp3.Id = 3
	//与其它主要编程语言的差异,通过实例的指针访问成员不需要使用"->", 用"."就可以
	emp3.Name = "Jane"
	emp3.Age = 18

	//方案1和方案2创建的都是 Employee 结构体类型
	t.Logf("emp1's type is: %T", emp1)	//emp1's type is: encapsulation.Employee
	//方案1和方案2,如果加上了取址符后,就和方案3是一样的效果了
	t.Logf("emp2 add after & type is: %T", &emp2)	//emp2 add after & type is: *encapsulation.Employee
	//方案3创建的是 Employee 结构体的指针类型
	t.Logf("emp3's type is: %T", emp3)	//emp3's type is: *encapsulation.Employee
	//通过实例的指针访问成员依旧不需要使用"->", 用"."就可以
	t.Logf("emp3.Name = %s", emp3.Name)	//emp3.Name = Jane
}

4、行为方法的创建和定义

a、方案1,实例对应方法被调用时,实例的成员会进行值复制

//直接对 Employee 这个结构体类型定义行为,它的行为是一个方法,
//它和普通方法的差别在于,会在方法名前面有一个(emp Employee)类型的声明,
//之所有有这么一小段声明,为了方便我们使用这个实例来访问我们封装的数据
func (emp Employee) employeeBehavior() string {
	return fmt.Sprintf("Id:%d - Name:%s - Age:%d", emp.Id, emp.Name, emp.Age)
}

b、方案2,采用指针的方案,实例成员不会进行值复制(推荐

//通常情况下为了避免内存拷贝,我们使用这种指针的方式
func (emp *Employee) empBehavior() string {
	return fmt.Sprintf("Id:%d - Name:%s - Age:%d", emp.Id, emp.Name, emp.Age)
}

c、对比对两种行为方法的不同

package encapsulation

import (
	"fmt"
	"testing"
	"unsafe"
)

//和 c 一样,用关键字 struct 定义结构体,然后把数据成员定义在花括号中
type Employee struct {
	Id int
	Name string
	Age int
}

//直接对 Employee 这个结构体类型定义行为,它的行为是一个方法,
//它和普通方法的差别在于,会在方法名前面有一个(emp Employee)类型的声明,
//之所有有这么一小段声明,为了方便我们使用这个实例来访问我们封装的数据
func (emp Employee) emp1Behavior() string {
	fmt.Printf("After run emp1Behavior(), emp.Id's address is: %x\n",
		unsafe.Pointer(&emp.Id))
	//把 Employee 的成员进行格式化输出
	return fmt.Sprintf("Id:%d,Name:%s,Age:%d", emp.Id, emp.Name, emp.Age)
}

//通常情况下为了避免内存拷贝,我们使用这种指针的方式
func (emp *Employee) emp2Behavior() string {
	fmt.Printf("After run emp2Behavior(), emp.Id's address is: %x\n",
		unsafe.Pointer(&emp.Id))
	//把 Employee 的成员进行格式化输出
	return fmt.Sprintf("Id:%d,Name:%s,Age:%d", emp.Id, emp.Name, emp.Age)
}

//操作结构体,调用结构体的行为方法,并比较两种行为方法定义方式的区别
func TestStructOperations(t *testing.T)  {
	emp1 := Employee{Id:4, Name:"Rose", Age:18}
	fmt.Printf("Before run emp1Behavior(), emp.Id's address is: %x\n",
		unsafe.Pointer(&emp1.Id))
	//可以直接调用实例的行为方法
	t.Log(emp1.emp1Behavior())	//Id:4,Name:Rose,Age:18

	//输出结果:
	//Before run emp1Behavior(), emp.Id's address is: c00000c1a0
	//After run emp1Behavior(), emp.Id's address is: c00000c1c0
	//我们会发现,emp.Id 的地址在行为方法调用前后,内存地址发生了变化,
	//说明这种写法会发生内存拷贝,有更大的内存开销



	emp2 := &Employee{Id:4, Name:"Rose", Age:18}
	fmt.Printf("Before run emp2Behavior(), emp.Id's address is: %x\n",
		unsafe.Pointer(&emp2.Id))
	//可以直接调用实例的行为方法
	t.Log(emp2.emp2Behavior())	//Id:4,Name:Rose,Age:18

	//输出结果:
	//Before run emp2Behavior(), emp.Id's address is: c00000c220
	//After run emp2Behavior(), emp.Id's address is: c00000c220
	//我们会发现,emp.Id 的地址在行为方法调用前后,内存地址是同一个,
	//说明这种写法不会发生内存拷贝,更值得推荐
}

:这篇博文是我学习中的总结,如有转载请注明出处:

https://blog.csdn.net/DaiChuanrong/article/details/118071519

上一篇Go 函数

下一篇Go面向对象-接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值