go语言【结构体-封装】

一. type

1. 作用

1. 给一个类型定义 别名, 实际上为什么会有byte, 就是我为了强调我们现在处理的对象是字节类型 这种别名实际上还是为了代码的可读性, 这个实际上本质上仍然是uint8 无非就是在代码编码阶段可读性强而已

2. 第二种 就是基于一个已有的类型定义一个新的类型

3. 定义结构体

4. 定义接口

5. 定义函数别名

 

2. 代码示例

func main() {
//go语言中的关键词 type
//1. 给一个类型定义 别名, 实际上为什么会有byte, 就是我为了强调我们现在处理的对象是字节类型 这种别名实际上还是为了代码的可读性, 这个实际上本质上仍然是uint8 无非就是在代码编码阶段可读性强而已
type myByte = byte
var b uint8
fmt.Printf("%T\n", b)
//2. 第二种 就是基于一个已有的类型定义一个新的类型
type myInt int
var i myInt
fmt.Printf("%T\n", i)
//3. 定义结构体
//4. 定义接口
//5. 定义函数别名
}

 

二. 结构体定义

1. 定义结构体及实例化- kv形式

大小写:

大小写在go语言中的重要性 可见性
一个包中的变量或者结构体如果首字母是小写 那么对于另一个包不可见
机构体定义的 名称 以及属性首字母大写很重要

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//go语言不支持面向对象
//面向对象的三个基本特征: 1. 封装 2. 继承 3. 多态 4. 方法重载 4. 抽象基类
//定义struct go语言没有class这个概念 所以说对于很多人来说会少理解很多面向对象抽象的概念
//1. 实例化- kv形式
var c Course = Course{
Name: "django",
Price: 100,
Url: "https://www.123.com",
}
//访问
fmt.Println(c.Name, c.Price, c.Url)
}

2. 定义结构体及实例化- 顺序形式

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//2. 第二种实例化方式 - 顺序形式
c2 := Course{"scrapy", 110, "https://www.imooc.com"}
fmt.Println(c2.Name, c2.Price, c2.Url)
}

 

3. 定义结构体及实例化-如果一个指向结构体的指针

如果一个指向结构体的指针, 通过结构体指针获取对象的值, 让很多人莫名其妙
应该能看出来 go语言实际上在借鉴动态语言的特性 - 很多地方不管如何写都是正确的
另一个根本的原因 - go语言的指针是受限的
fmt.Println(c3.Name, c3.Price, c3.Url) 这里其实是go语言的一个语法糖 go语言内部会将c3.Name转换成 (*c3).Name

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//3. 如果一个指向结构体的指针, 通过结构体指针获取对象的值, 让很多人莫名其妙
c3 := &Course{"tornado", 100, "https://www.imooc.com"}
//fmt.Printf("%T", c3)
//应该能看出来 go语言实际上在借鉴动态语言的特性 - 很多地方不管如何写都是正确的
//另一个根本的原因 - go语言的指针是受限的
fmt.Println(c3.Name, c3.Price, c3.Url) //这里其实是go语言的一个语法糖 go语言内部会将c3.Name转换成 (*c3).Name
}
 

4. 定义结构体及实例化-结构体的零值

如果不给结构体赋值, go语言会默认给每个字段采用默认值
package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//零值 如果不给结构体赋值, go语言会默认给每个字段采用默认值
c4 := Course{}
fmt.Println(c4.Price)
// 输出:0
}

 

5. 定义结构体及实例化-多种方式零值初始化结构体

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//5. 多种方式零值初始结构体
var c5 Course = Course{}
var c6 Course
var c7 *Course = &Course{}
var c8 *Course
//为什么c6和c8表现出来的结果不一样 指针如果只申明不赋值 默认值是nil c6不是指针 是结构体的类型
//slice map
fmt.Println("零值初始化")
fmt.Println(c5.Price)
fmt.Println(c6.Price)
fmt.Println(c7.Price)
fmt.Println(c8.Price)
//输出
//0
//0
//0
//报错
}

 

6. 定义结构体及实例化-结构体是值类型

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//6. 结构体是值类型
c8 := Course{"scrapy", 110, "https://www.imooc.com"}
c9 := c8
fmt.Println(c8)
fmt.Println(c9)
c8.Price = 200
fmt.Println(c8) //200
fmt.Println(c9) //100
}

7. 定义结构体及实例化-结构体的大小

package main
import (
"fmt"
"unsafe"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//7. 结构体的大小 占用内存的大小 可以使用sizeof来查看对象占用的类型
fmt.Println(unsafe.Sizeof(1))
//go语言string的本质 其实string是一个结构体
fmt.Println(unsafe.Sizeof(""))
fmt.Println(unsafe.Sizeof(c8))
}

 

7. 定义结构体及实例化-slice的大小

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func main() {
//8. slice的大小
type slice struct {
array unsafe.Pointer // 底层数组的地址
len int // 长度
cap int // 容量
}
s1 := []string{"django", "tornado", "scrapy", "celery", "snaic", "flask"}
fmt.Println("切片占用的内存:", unsafe.Sizeof(s1))
//24
m1 := map[string]string{
"bobby1": "django",
"bobby2": "tornado",
"bobby3": "scrapy",
"bobby4": "celery",
}
fmt.Println(unsafe.Sizeof(m1))
//8
}

三. 结构体-绑定方法

golang语言是怎么实现python class的调用方式呢

函数的接收者, 在函数的前面加上c Course,其实就是将strut将函数绑定起来了, c 相当与python类对象中的self

type Course struct {
Name string
Price int
Url string
}
func (c Course) printCourseInfo() {
fmt.Printf("课程名:%s, 课程价格: %d, 课程的地址:%s", c.Name, c.Price, c.Url)
}

怎么实现以上的调用呢

//结构体方法, 达到了封装数据和封装方法的效果
c10 := Course{"scrapy", 110, "https://www.imooc.com"}
c10.printCourseInfo() //就完成了函数的调用

但是会出现一个问题,当我们去修改price的值后会发生什么

可见,输出110,实际并没有改变值,这是为什么呢 

c10.setPrice(300)的本质是 Course.setPrice(c10, 300) , c10.setPrice(300)其实是语法糖,是内部都进行转换的。 而函数的参数是怎么传递的,取决于c10的类型是什么类型,c10是结构体,是值传递, 意味着传递进去后会c10会被复制一份,所以是修改不了的

然后怎么来修改呢

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
func (c Course) printCourseInfo() {
fmt.Printf("课程名:%s, 课程价格: %d, 课程的地址:%s", c.Name, c.Price, c.Url)
}
func (c Course) setPrice(price int) {
c.Price = price
}
func main() {
//结构体方法, 达到了封装数据和封装方法的效果
c10 := Course{"scrapy", 110, "https://www.imooc.com"}
//Course.setPrice(c10, 300) // 本质
c10.setPrice(300)
fmt.Println(c10.Price)
// 输出110
}

使用指针,可以进行值的修改

结构体的接收者有两种形式

1. 值传递

2. 指针传递 如果你想改结构体的值,就用指针, 如果结构体的数据很大,也使用指针,增加性能

package main
import (
"fmt"
)
type Course struct {
Name string
Price int
Url string
}
//1. 结构体的方法只能和结构体在同一个包中
//2. 内置的int类型不能加方法,但是可以自己定义类型 type Myint int
// 函数的接收者, 在函数的前面加上c Course,其实就是将strut将函数绑定起来了
func (c Course) printCourseInfo() {
fmt.Printf("课程名:%s, 课程价格: %d, 课程的地址:%s", c.Name, c.Price, c.Url)
}
func (c *Course) setPrice(price int) {
c.Price = price
}
func main() {
//结构体方法, 达到了封装数据和封装方法的效果
c10 := Course{"scrapy", 110, "https://www.imooc.com"}
c10.setPrice(300)
fmt.Println(c10.Price)
(&c10).setPrice(200) //修改c10的price? 为什么呢? 语法糖 函数参数的传递是怎么传递的? 结构体是值传递
c10.setPrice(210) // 和上面的调用方式结果其实是一样的,go内部其实做了转换
fmt.Println(c10.Price)
c10.printCourseInfo()
//结构体的接收者有两种形式 1. 值传递 2. 指针传递 如果你想改结构体的值,就用指针, 如果结构体的数据很大,也使用指针,增加性能
//go语言不支持继承 但是有办法能达到同样的效果 组合
}

完整测试代码

package main
import (
"fmt"
"unsafe"
)
// Course类名,struct关键字
type Course struct {
Name string
Price int
Url string
}
//函数的接收者,将Course struct进行绑定
func (c Course) printCourseInfo() {
fmt.Printf("课程名:%s, 课程价格: %d, 课程的地址:%s", c.Name, c.Price, c.Url)
}
//修改价格
func (c *Course) setPrice(price int) {
c.Price = price
}
//1. 结构体的方法只能和结构体在同一个包中
//2. 内置的int类型不能加方法
func main() {
//go语言不支持面向对象
//面向对象的三个基本特征: 1. 封装 2. 继承 3. 多态 4. 方法重载 4. 抽象基类
//定义struct go语言没有class这个概念 所以说对于很多人来说会少理解很多面向对象抽象的概念
//1. 实例化- kv形式
var c Course = Course{
Name: "django",
Price: 100,
Url: "https://www.imooc.com",
}
//访问
fmt.Println(c.Name, c.Price, c.Url)
//大小写在go语言中的重要性 可见性
//一个包中的变量或者结构体如果首字母是小写 那么对于另一个包不可见
//机构体定义的 名称 以及属性首字母大写很重要
//2. 第二种实例化方式 - 顺序形式
c2 := Course{"scrapy", 110, "https://www.imooc.com"}
fmt.Println(c2.Name, c2.Price, c2.Url)
//3. 如果一个指向结构体的指针, 通过结构体指针获取对象的值, 让很多人莫名其妙
c3 := &Course{"tornado", 100, "https://www.imooc.com"}
//fmt.Printf("%T", c3)
//应该能看出来 go语言实际上在借鉴动态语言的特性 - 很多地方不管如何写都是正确的
//另一个根本的原因 - go语言的指针是受限的
fmt.Println(c3.Name, c3.Price, c3.Url) //这里其实是go语言的一个语法糖 go语言内部会将c3.Name转换成 (*c3).Name
//4. 零值 如果不给结构体赋值, go语言会默认给每个字段采用默认值
c4 := Course{}
fmt.Println(c4.Price)
//5. 多种方式零值初始结构体
var c5 Course = Course{}
var c6 Course
var c7 *Course = &Course{}
//或者
//var c7 *Course = new(Course)
//var c8 *Course
//为什么c6和c8表现出来的结果不一样 指针如果只申明不赋值 默认值是nil c6不是指针 是结构体的类型
//slice map
fmt.Println("零值初始化")
fmt.Println(c5.Price)
fmt.Println(c6.Price)
fmt.Println(c7.Price)
//6. 结构体是值类型
c8 := Course{"scrapy", 110, "https://www.imooc.com"}
c9 := c8
fmt.Println(c8)
fmt.Println(c9)
c8.Price = 200
//为值传递,不影响之前的赋值
fmt.Println(c8)
fmt.Println(c9)
//go语言中struct无处不在
//7. 结构体的大小 占用内存的大小 可以使用sizeof来查看对象占用的类型
fmt.Println(unsafe.Sizeof(1))
//go语言string的本质 其实string是一个结构体
fmt.Println(unsafe.Sizeof(""))
fmt.Println(unsafe.Sizeof(c8))
//8. slice的大小
type slice struct {
array unsafe.Pointer // 底层数组的地址
len int // 长度
cap int // 容量
}
s1 := []string{"django", "tornado", "scrapy", "celery", "snaic", "flask"}
fmt.Println("切片占用的内存:", unsafe.Sizeof(s1))
m1 := map[string]string{
"bobby1": "django",
"bobby2": "tornado",
"bobby3": "scrapy",
"bobby4": "celery",
}
fmt.Println(unsafe.Sizeof(m1))
//结构体方法, 达到了封装数据和封装方法的效果
c10 := Course{"scrapy", 110, "https://www.imooc.com"}
//Course.setPrice(c10, 200) 本质是这样的
(&c10).setPrice(200) //修改c10的price? 为什么呢? 语法糖 函数参数的传递是怎么传递的? 结构体是值传递,所以通过 c10.setPrice(200)修改是无效的,需要将fun修改为指针 func (c *Course)
//c10.setPrice(200)和(&c10).setPrice(200) 调用效果是一样的,结构体是内部自己将指针做装换
fmt.Println(c10.Price)
c10.printCourseInfo()
//结构体的接收者有两种形式 1. 值传递 2. 指针传递 应用:如果你想改结构体的值 如果结构体的数据很大
//go语言不支持继承 但是有办法能达到同样的效果 组合
}

 

posted @   wanghhhh  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言
点击右上角即可分享
微信分享提示