Golang面向对象编程(一)

文章目录

  • 结构体
    • 基本介绍
    • 结构体定义方式
    • 创建结构体变量
    • 结构体内存对齐
    • 结构体类型转换
    • 字段的Tag标签
  • 方法
    • 基本介绍
    • 方法的定义和调用
    • 方法调用的传参机制
    • String方法

结构体

基本介绍

基本介绍

  • Go支持面向对象编程特性,包括封装、继承和多态,但Go中没有类(class)而是基于结构体(struct)来实现OOP特性的。
  • 结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体,结构体中的成员称为结构体的字段。
  • 在编程过程中,通常将一类事物的特性提取出来,形成一个结构体类型,然后基于这个结构体类型创建出多个实例。

结构体定义方式

结构体定义方式

Go中结构体定义的基本语法如下:

在这里插入图片描述

使用案例如下:

// 定义结构体
type Student struct {
	Name   string
	Age    int
	Gender string
}

结构体是值类型,不同结构体变量的字段是独立的,互不影响。如下:

在这里插入图片描述

说明一下:

  • 结构体中字段的类型可以是任意类型,包括基本数据类型、数组、引用类型以及自定义类型。

创建结构体变量

创建结构体变量

方式一: 指明结构体的类型,结构体字段采用对应的默认值。

var stu1 Student
fmt.Printf("stu1 = %v\n", stu1)      // stu1 = { 0 }
fmt.Printf("stu1 type = %T\n", stu1) // stu1 type = main.Student

方式二: 指明结构体的类型,并初始化结构体字段。

var stu2 = Student{"Alice", 12, "女"}
fmt.Printf("stu2 = %v\n", stu2)      // stu2 = {Alice 12 女}
fmt.Printf("stu2 type = %T\n", stu2) // stu2 type = main.Student

方式三: 指明结构体的类型,并通过字段名的方式初始化结构体字段。

var stu3 = Student{
	Name:   "Alice",
	Age:    12,
	Gender: "女",
}
fmt.Printf("stu3 = %v\n", stu3)      // stu3 = {Alice 12 女}
fmt.Printf("stu3 type = %T\n", stu3) // stu3 type = main.Student

方式四: 通过new函数创建指定类型的结构体变量,得到指向结构体变量的指针。

var stu4 = new(Student)
fmt.Printf("stu4 = %v\n", stu4)      // stu4 = &{ 0 }
fmt.Printf("stu4 type = %T\n", stu4) // stu4 type = *main.Student

方式五: 指明结构体的类型,并初始化结构体字段,得到指向结构体变量的指针。

var stu5 = &Student{"Alice", 12, "女"}
fmt.Printf("stu5 = %v\n", stu5)      // stu5 = &{Alice 12 女}
fmt.Printf("stu5 type = %T\n", stu5) // stu5 type = *main.Student

方式六: 指明结构体的类型,并通过字段名的方式初始化结构体字段,得到指向结构体变量的指针。

var stu6 = &Student{
	Name:   "Alice",
	Age:    12,
	Gender: "女",
}
fmt.Printf("stu6 = %v\n", stu6)      // stu6 = &{Alice 12 女}
fmt.Printf("stu6 type = %T\n", stu6) // stu6 type = *main.Student

说明一下:

  • 在创建结构体变量并初始化结构体字段时,如果通过字段名的方式对结构体字段进行初始化,那么可以不需要对所有字段都进行初始化,没有初始化的字段将会采用对应的默认值,否则需要对结构体所有字段进行初始化,并且初始化的顺序必须与结构体字段的定义顺序相同。

结构体内存对齐

结构体内存对齐规则

Go中结构体的大小遵循结构体的对齐规则:

  1. 结构体中第一个字段,对齐到结构体的首地址处。
  2. 结构体中的其他字段,对齐到各自对齐数的整数倍的地址处。
  3. 结构体的总大小为最大对齐数(每个字段都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体,嵌套结构体对齐到自己的最大对齐数的整数倍处。

其中,不同类型字段的对齐数可能不同,通过unsafe包中的Alignof函数可以获取指定类型对应的对齐数。

结构体大小计算案例

下面代码中分别获取了Student结构体中各个字段的对齐数和大小。如下:

package main

import (
	"fmt"
	"unsafe"
)

type Student struct {
	Name   string
	Age    uint8
	Gender string
}

func main() {
	var stu = Student{"Alice", 12, "女"}
	fmt.Printf("Name align number = %d\n", unsafe.Alignof(stu.Name))     // Name align number = 8
	fmt.Printf("Name size = %d\n", unsafe.Sizeof(stu.Name))              // Name size = 16
	fmt.Printf("Age align number = %d\n", unsafe.Alignof(stu.Age))       // Age align number = 8
	fmt.Printf("Age size = %d\n", unsafe.Sizeof(stu.Age))                // Age size = 1
	fmt.Printf("Gender align number = %d\n", unsafe.Alignof(stu.Gender)) // Gender align number = 8
	fmt.Printf("Gender size = %d\n", unsafe.Sizeof(stu.Gender))          // Gender size = 16
	
	fmt.Printf("stu align number = %d\n", unsafe.Alignof(stu))           // stu align number = 8
	fmt.Printf("stu size = %d\n", unsafe.Sizeof(stu))                    // stu size = 40
}

上述结构体变量在内存中的布局如下:

在这里插入图片描述

说明一下:

  • 图中的内存以字节为单位进行划分,内存旁边的小标号,表示当前位置相对于结构体起始位置的偏移量。
  • Name作为结构体中的第一个字段,对齐到结构体的首地址处,图中黄色部分表示Name字段占用的内存,共16字节。
  • Age字段对齐到8的整数倍处,图中粉红色部分表示Age字段占用的内存,共1字节。
  • Gender字段对齐到8的整数倍处,图中蓝色部分表示Gender字段占用的内存,共16字节。
  • 结构体的总大小为最大对齐数的整数倍,即8的整数倍,所以该结构体的大小为40字节。

结构体类型转换

结构体类型转换

Go中的结构体类型是自定义类型,自定义类型之间可以进行类型转换,但要求这两个自定义类型拥有完全相同的字段名、字段个数和字段类型,并且字段的声明顺序也必须相同。如下:

package main

import "fmt"

type Student struct {
	Name   string
	Age    int
	Gender string
}

type Person struct {
	Name   string
	Age    int
	Gender string
}

func main() {
	var per = Person{"Alice", 12, "女"}
	var stu Student = Student(per) // 类型转换
	fmt.Printf("stu = %v\n", stu)  // stu = {Alice 12 女}
}

Go中可以通过type给自定义类型取别名,但编译器会认为这是一个新的数据类型,在相互赋值时需要进行类型转换,无法直接赋值。如下:

package main

import "fmt"

type Student struct {
	Name   string
	Age    int
	Gender string
}

type Stu Student

func main() {
	var student = Student{"Alice", 12, "女"}
	var stu Stu = Stu(student)    // 类型转换
	fmt.Printf("stu = %v\n", stu) // stu = {Alice 12 女}
}

字段的Tag标签

字段的Tag标签

在定义结构体时,可以在每个结构体字段的后面设置tag标签,并用反引号将其包裹起来。tag标签可以用于存储与字段相关的信息,例如数据库列名、JSON序列化配置、表单验证等。如下:

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Gender string `json:"gender"`
}

func main() {
	// json序列化
	var stu1 = Student{"Alice", 12, "女"}
	data, err := json.Marshal(stu1)
	if err != nil {
		fmt.Printf("json serialize error, err = %v\n", err)
		return
	}
	fmt.Printf("json string = %v\n", string(data)) // json string = {"name":"Alice","age":12,"gender":"女"}

	// json反序列
	var stu2 Student
	fmt.Printf("stu2 = %v\n", stu2) // stu2 = { 0 }
	err = json.Unmarshal(data, &stu2)
	if err != nil {
		fmt.Printf("json unserialize error, err = %v\n", err)
		return
	}
	fmt.Printf("stu2 = %v\n", stu2) // stu2 = {Alice 12 女}
}

说明一下:

  • 标签的一般形式是key:"value",其中key表示标签的名称,value表示与该标签相关的值,多个标签可以用空格分隔,通过Go中的反射机制可以获取标签的信息。
  • 上述代码中通过字段标签,分别指定了结构体各个字段在JSON序列化时的名称,可以看到序列化后的JSON字符串中使用的字段名称就是标签中指定的名称,如果没有指定那么在JSON序列化时会默认使用字段名。
  • Marshal是encoding/json包中的函数,用于进行JSON序列化,该函数接收任意类型的参数,如果序列化成功,则序列化后的JSON字符串通过第一个返回值返回,如果序列化失败,则会通过第二个返回值返回错误原因。
  • Unmarshal是encoding/json包中的函数,用于进行JSON反序列化,该函数接收两个参数,第一个参数是待反序列化的字符串,第二个参数作为输出型参数接收反序列化的结果,如果反序列化失败,则通过返回值返回错误原因。
  • Marshal函数进行JSON序列化时,返回的JSON字符串是[]byte类型的,Unmarshal函数在进行JSON反序列化时,要求传入的JSON字符串也是[]byte类型的,在使用时注意进行类型转换。

方法

基本介绍

基本介绍

  • Go中的方法指的是与特定类型关联的函数,它们为类型提供了行为和操作方式,并允许类型的实例对这些方法进行调用。
  • Go语言规定方法和类型的定义必须在同一个包中,使得类型与方法紧密耦合在一起,这有助于保持代码的模块化和可读性。此外,将方法与类型定义在同一个包,保证了方法能够访问类型不可导出的字段和方法,并能有效避免其他包对类型的行为进行修改。

方法的定义和调用

方法的定义和调用

Go中方法定义的基本语法如下:

在这里插入图片描述

创建类型的实例后,通过实例.方法名的方式即可调用对应的方法。如下:

package main

import "fmt"

type Student struct {
	Name   string
	Age    int
	Gender string
}

func (stu Student) PrintAge() {
	fmt.Printf("age = %d\n", stu.Age)
}

func (stu *Student) AgeAdd() {
	stu.Age++
}

func main() {
	var stu = Student{"Alice", 12, "女"}
	stu.AgeAdd()
	stu.PrintAge() // Alice age = 13
}

说明一下:

  • 上述代码中定义了Student类型,并为该类型定义(绑定)了两个方法,分别是PrintAge和AgeAdd。
  • 在定义方法时,receiver type表示该方法与type类型进行绑定,当通过实例调用方法时会将实例传递给receiver。如果希望在方法中改变实例的值,需要将type设置为对应类型的指针类型。
  • 方法的访问控制与函数相同,方法名首字母小写只能在本包中访问,方法名首字母大写可以在本包和其他包访问。
  • 通过结构体指针访问结构体字段时,Go语言会自动对指针进行解引用操作,因此代码中的stu.Age++等价于(*stu).Age++

方法调用的传参机制

方法调用的传参机制

在Go中,不仅可以通过实例来调用其绑定的方法,通过实例的指针同样能够完成方法的调用。如下:

package main

import "fmt"

type Student struct {
	Name   string
	Age    int
	Gender string
}

func (stu Student) PrintAge() {
	fmt.Printf("%s age = %d\n", stu.Name, stu.Age)
}

func (stu *Student) AgeAdd() {
	stu.Age++
}

func main() {
	// 结构体变量调用方法
	var stu1 = Student{"Alice", 12, "女"}
	stu1.AgeAdd()
	stu1.PrintAge() // Alice age = 13

	// 结构体指针调用方法
	var stu2 = &Student{"Bob", 14, "男"}
	stu2.AgeAdd()
	stu2.PrintAge() // Bob age = 15
}

说明一下:

  • 通过实例和实例的指针均能完成方法的调用,但在调用方法时,实例具体的传参形式由对应方法的绑定类型决定。
  • 如果被调用方法绑定的是类型,则调用方法时实例以值拷贝的方式传递给方法中的receiver参数;如果被调用方法绑定的是类型的指针,则调用方法时实例以地址拷贝的方式传递给方法中的receiver参数。

String方法

String方法

在Go中,如果一个类型实现了String方法,那么在使用fmt包中的函数打印该类型变量时,就会输出String方法返回的字符串。如下:

package main

import "fmt"

type Student struct {
	Name   string
	Age    int
	Gender string
}

func (stu Student) String() string {
	return fmt.Sprintf("%s是一个%d岁的%s孩\n", stu.Name, stu.Age, stu.Gender)
}

func main() {
	var stu = Student{"Alice", 12, "女"}
	fmt.Printf("stu = %v\n", stu) // stu = Alice是一个12岁的女孩
}

说明一下:

  • 在使用fmt包中的函数打印变量时,如果该变量类型实现了String方法,那么在打印变量时会通过调用String方法来获取字符串并输出,否则会根据变量的类型进行默认的格式化操作并输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/611489.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

技术创作者在千帆AppBuilder中获得的极致体验

目录 前言 千帆AppBuilder简介 传统的技术文章写作方式 借助千帆AppBuilder提高写作质量和效率 千帆AppBuilder详细搭建步骤 1、注册百度智能云账号 2、登录百度智能云控制台 3、创建千帆AppBuilder应用 4、配置千帆AppBuilder应用 5、调试和发布千帆AppBuilder应用 …

文章分享:《肿瘤DNA甲基化标志物检测及临床应用专家共识(2024版)》

本文摘自于《肿瘤DNA甲基化标志物检测及临床应用专家共识(2024版)》 目录 1. DNA甲基化标志物概述 2 DNA甲基化标志物的临床检测 2.1 临床样本前处理注意事项 2.2 DNA甲基化标志物检测技术方法 2.2.1 DNA提取与纯化 2.2.2 DNA转化 2.2.3 DNA 甲基…

综述列表(~2024.05.10)

(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 每周末更新,完整版进群获取。 Q 群在群文件,VX 群每周末更新。

day5Qt作业

服务器端 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//准备组件,初始化组件状态this->setFixedSize(800,600);chatwidget new QListWidge…

使用Matplotlib绘制正弦和余弦函数曲线

前言 在数据可视化领域,Matplotlib是一个功能强大的Python库,它允许用户创建各种静态、交互式和动画图形。本文将引导您通过一个简单的示例,学习如何使用Matplotlib绘制正弦和余弦函数曲线。 第一步:导入必要的库: …

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)是期刊“NEURAL COMPUTING & APPLICATIONS”(IF 6.0)的2021年智能优化算法 01.引言 【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)用于解决约束和全局优化问…

【JVM】垃圾回收机制(Garbage Collection)

目录 一、什么是垃圾回收? 二、为什么要有垃圾回收机制(GC)? 三、垃圾回收主要回收的内存区域 四、死亡对象的判断算法 a)引用计数算法 b)可达性分析算法 五、垃圾回收算法 a)标记-清除…

OSPF虚链路

原理概述 通常情况下,一个OSPF网络的每个非骨干区域都必须与骨干区域通过ABR路由器直接连接,非骨干区域之间的通信都需要通过骨干区域进行中转。但在现实中,可能会因为各种条件限制,导致非骨干区域和骨干区域无法直接连接&#x…

Redis缓存雪崩,击穿,穿透问题

缓存雪崩、击穿、穿透、发生的背景 ​ 作者最近在写一个社区论坛项目,初始设想将论坛里用户发布的帖子内容存到数据库中,当用户访问论坛里的帖子时,帖子信息都从数据库中查。众所周知数据库的帖子数据是存在磁盘中的,而磁盘读写数…

串口初始化自己独立的见解--第九天

1.SM0,SM1 我们一般用 8位UART,波特率可变 (方式1的工作方式) SCON :SM2 一般不用,SM0 0 ,SM1 1 PCON : 有两位 我们不动它,不加速,初始值 TMOD:8位自动重装定时器&#xff0…

ADS基础教程11 - TouchStone文件的导出及导入

目录 一、 T o u c h S t o n e 介绍 \color{#4285f4}{ \mathbf{ 一、TouchStone介绍}} 一、TouchStone介绍 二、文件导出、导入方式 \color{#4285f4}{ \mathbf{ 二、文件导出、导入方式}} 二、文件导出、导入方式1.原理图操作1)原理图中导出2.原理图中导入 3.DDW中…

Python图形复刻——绘制母亲节花束

各位小伙伴,好久不见,今天学习用Python绘制花束。 有一种爱,不求回报,有一种情,无私奉献,这就是母爱。祝天下妈妈节日快乐,幸福永远! 图形展示: 代码展示: …

详解分布式锁

知识点: 单体锁存在的问题: 单体锁,即单体应用中的锁,通过加单体锁(synchronized或RentranLock)可以保证单个实例并发安全 单体锁是JVM层面的锁,只能保证单个实例上的并发访问安全 如果将单…

消除试卷手写笔迹的软件免费的有哪些?这几款都不错

消除试卷手写笔迹的软件免费的有哪些?在数字化学习的浪潮中,学生们越来越频繁地利用电子设备来完成学习任务。然而,当纸质试卷需要被数字化并再次利用时,如何高效地消除手写笔迹便成为了一个有待解决的问题。那么,今天…

【Linux】简易进度条的实现

🎉博主首页: 有趣的中国人 🎉专栏首页: Linux 🎉其它专栏: C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好,本片文章将会讲解Linux中进度条的实现的相关内容。 如果看到最后您觉得这篇文章写得…

coherence protocal基础

一致性整体抽象 coherence protocol是通过保证如下的两点invariants,来实现一致性的; SWMR invariant, 对于任何一个地址,任何时刻,都只会有一个core写,可能会有多个core读;Data-value invariant&#xf…

每天五分钟深度学习:数学中的极值

本文重点 在数学领域中,极值是一个极其重要的概念,它不仅在纯数学理论研究中占据核心地位,而且在工程、物理、经济等实际应用领域也发挥着不可替代的作用。极值问题涉及函数的最大值和最小值,是微积分学中的一个基本问题。本文旨在详细介绍数学中的极值概念、性质、求解方…

Python查询PostgreSQL数据库

哈喽,大家好,我是木头左! Python与PostgreSQL的连接 需要了解如何在Python中连接到PostgreSQL数据库。这通常涉及到使用一个库,如psycopg2,它是Python中用于PostgreSQL的最流行的适配器。安装psycopg2非常简单&#x…

202466读书笔记|《一天一首古诗词》——借问梅花何处落,风吹一夜满关山

202466读书笔记|《一天一首古诗词》——借问梅花何处落,风吹一夜满关山 上册下册 《一天一首古诗词》作者李锡琴,蛮早前加入书架的已购买书籍,这次刚好有点时间,利用起来读完。 赏析没有细看,只读了诗词部分&#xff0…

Cocos creator实现《战机长空》关卡本地存储功能

Cocos creator实现《战机长空》关卡本地存储功能 Cocos creator在开放小游戏过程中,经常会出现设置关卡,这里记录一下关卡数据本地存储功能。 一、关卡设置数据 假如我们有关卡数据如下, let settings [ { level: 1, // 第1关 score: 0,…
最新文章