【Go基础】结构体

news/2024/11/20 0:43:07/

1. 结构体引入

Golang也支持面向对象编程,但是和传统的面向对象有区别,并不是像Java、C++那样纯粹的面向对象语言,而是通过特别的手段实现面向对象特点。

Golang没有类(Class)的概念,但是提供了结构体(struct),和其他编程语言中的类(class)有同等地位,可以理解为 :Go基于struct"拐弯抹角"的实现了面向对象的特点。

Golang面向对象编程非常简洁,去掉了传统OOP语言的 方法重载、构造函数、和析构函数、this、super一系列东西,但是保留了封装、继承、多态的特性,只是和其他语言的实现方式不同。

2. 结构体的定义

使用 type​ 和 struct​定义一个结构体

type People struct {
}

People就成了一个结构体,当然可以为People添加一些字段

type People struct {name stringage stringaddr string
}

定义之后如何声明呢 ?

有以下几种方式 :

  1. 使用 var 变量名 结构体类型

    var people People
    people.name = "小明"
    people.age = 18
    fmt.Println(people) 
    // {小明 18}
    
  2. 变量名 := new(结构体类型)

    new返回的指向特定类型的指针,所以这种方式返回的是 指向一个People结构体 的指针

    people := new(People)
    people.name = "小明"
    people.age = 18
    fmt.Println(people) 
    // &{小明 18}
    

    为什么people是指针,还能使用 .​ 来访问结构体中的变量呢?使用 (*people).name 、(*people).age​当然可以访问,只是这种不好写,Go为我们进行了优化。

    如果第一种方式和这种方式让你选一种,你用哪一个呢?当一个结构体中的字段特别多时,直接创建该结构体会占用大量空间,但是指针不一样,指针固定大小。所以以上两种第二种常用。不过还有其他方式。

  3. 变量名 := 结构体类型{字段1:值, 字段1:值,}

    使用这种方式跟第一种差不多,都是直接开辟空间,不过这种方式可以直接对属性赋值

    (左边用var​还是:=​都一样)

    people := People{name: "小明",age: 18,
    }
    fmt.Printf("people的类型为: %T\n", people)
    // main.People// 这样也一样
    var people1 People = People{name: "小明",age: 18,
    }
    

    这可不是json,字段名不加双引号,最后一个字段后要要加逗号。

  4. 变量名 := &结构体类型{字段1:值, 字段2:值,}

    这种方式和第二种像,返回的是一个指向结构体的指针。

    people := &People {name: "小明",age: 18,
    }
    fmt.Printf("people的类型为: %T\n", people)
    //  *main.People
    

这四种方式选择哪一种,这要看习惯与需求,熟练用其中一种,可以看懂其他的方式就行。

初始化时可以直接列出全部的字段值,这种方式的缺点是 :无法指定字段,且一次性需要给全部字段赋值,如果后续由增加或删除,会出问题。

type People struct {name stringage int
}
func main() {people := &People {"小明",18,}
}

匿名字段 :没有名字的字段。

type People struct {stringint
}
func main() {people := &People {"小明",18}}

赋值时需要严格按照字段类型与顺序,一般不用匿名字段,因为可读性太差,且如果出现多个string int类型,可读性更差

3. 结构体之间的转换

当两个不同类型的结构体中,字段数量字段名字段顺序字段类型全部相同时,这两个字段可以相互转换。

type People struct {name   stringage    intheight float32
}type Monkey struct {name   stringage    intheight float32
}func main() {// 声明一个People类型的结构体people := &People{name:   "小明",age:    18,height: 180.4,}// 将上述结构体变量转换为 *Monkey 类型monkey := (*Monkey)(people)fmt.Println(monkey)// 打印结果:  &{小明 18 180.4}}

两个结构体是否能相互转换 :

字段数、字段名、字段顺序、字段类型​ 全部相同,缺一个就无法编译。

4. 结构体嵌套

说人话,结构体里还有结构体。

type Address struct {province stringcity     string
}
type People struct {id      intname    stringage     intaddress Address
}
func main() {people := &People{id:   1,name: "小明",age:  18,address: Address{province: "河北",city:     "衡水",},}fmt.Println(people)
}

需要注意的是,People中的address字段如果是指针,在赋初值时就申请指针,是结构体就直接赋值结构体。

可以直接people.address.city访问嵌套结构体中的值.

fmt.Println(people.address.city)

这个经常使用,因为仅仅使用一个结构体无法描述出一个复杂的类型,就可以分为多个字段。

结构体中也可以有结构体数组,不过很少用这种方式一个一个new,都是从前端传来的json直接转换。

package mainimport ("fmt""strconv"
)type Phone struct {name  stringprice int
}func NewPhone(name string, price int) Phone {return Phone{name:  name,price: price,}
}type People struct {name   stringphones []Phone
}func NewPeople(name string, phones []Phone) People {return People{name:   name,phones: phones,}
}func main() {// 创建一个People数组people := NewPeople("小明", make([]Phone, 5, 200))// 循环给people数组中的值赋值for i := 0; i < 5; i++ {phoneName := "小米" + strconv.Itoa(i)phone := NewPhone(phoneName, 1000*(i+1))//people.phones[i] = phone// 如果在初始化切片时指定长度为0或者小于5,使用append()扩容// people.phones = append(people.phones, phone)}fmt.Println(people)}

5. 方法的引入

方法是作用在指定的数据类型上、和指定的数据类型绑定,因此自定义类型都可以有方法,通常给结构体定义一些方法,以便外部调用,就像Java中的setter/getter方法、C++中的构造函数、析构函数一样。当然,Go语言中的方法并没有放在结构体中,而是和结构体分离。

形式:

func (变量名 : 自定义类型) 方法名(参数列表) (返回值列表) {

// 方法体

}

第一个括号中的变量不需要传入,而是==“谁调用,这个变量就是谁”==

当然可以不使用第一个变量。

给People定义一个初始化方法:

type People struct {name stringage int
}
func newPeople(name string, age int) (*People) {return &People {people.name = namepeople.age = age}
}
func main() {people := newPeople("小明", 18)
}

这个newPeople方法接收两个变量,name和age,通过这两个变量创造一个Person对象并返回,以后再也不用频繁的写初始化方法了。

以下是使用第一个变量的例子 :

func (people *People) SetAge(age int) {people.age = age
}

这个方法的调用者必须是People类型的指针,否则会报错。

people := newPeople("小明", 18)
fmt.Println(people) // &{小明 18}
people.SetAge(10)
fmt.Println(people.age) // 10

就是方法的特殊性,让我们可以对结构体进行各种操作。你可以单独定义一个文件,在这个文件中完成对结构体的封装。

image

方法和函数的区别 :

  • 方法被调用者影响。
  • 函数与没有具体的调用者。


http://www.ppmy.cn/news/12578.html

相关文章

装修--避坑--厨房

瓷砖胶 大砖上墙要用瓷砖胶&#xff0c;瓷砖胶要看执行标准&#xff0c;瓷砖边长大于90公分的&#xff0c;至少要用C2型&#xff0c;沾性能力更强边长小于90公分的&#xff0c;至少要用C1型。C0型只适合边长80公分一下的陶瓷砖&#xff0c;用错了后期瓷砖照样脱落背胶是过时的…

【爬虫】第七部分 scrapy

【爬虫】第七部分 scrapy 文章目录【爬虫】第七部分 scrapy7. scrapy7.1 基本使用7.2 项目的文件结构7.3 response的方法和属性7.4 小案例7.5 scrapy 工作原理7.6 管道的使用7.7 多管道下载7.8 下载分页类型和get请求的使用7.9 下载多层级类型7.10 post请求的使用总结7. scrapy…

c语言实现扫雷(详细讲解)

本篇介绍,讲解如何使用c语言实现扫雷小游戏. 金句分享: ✨✨✨爱你所爱,行你所行✨✨✨ 目录前言:一、游戏设计思路介绍:效果展示二、游戏的分步讲解2.1、主函数测试区&#xff08;test.c&#xff09;基本构成2.2、游戏中函数实现区(game.c) (重点)2.21、雷盘的创建与初始化函…

【数据结构】手撕八大排序算法

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《数据结构》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1.排序的概念&#xff1a; 2.八大排序的思路及其细节 2.1直接插入排序 …

java springboot 项目构建报错解决办法

这里总结了一些常见的springboot 项目构建报错及解决办法 错误: 无效的源发行版:17 错误原因 build.gradle 文件中可以通过下面两项来指定项目运行所需的jdk版本 sourceCompatibility:指定编译编译.java文件的jdk版本 targetCompatibility&#xff1a;确保class文件与target…

回归预测 | MATLAB实现SSA-LSSVM麻雀算法优化最小二乘支持向量机多输入单输出

回归预测 | MATLAB实现SSA-LSSVM麻雀算法优化最小二乘支持向量机多输入单输出 目录回归预测 | MATLAB实现SSA-LSSVM麻雀算法优化最小二乘支持向量机多输入单输出预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 MATLAB实现SSA-LSSVM麻雀算法优化最小二乘支持向量机…

spring系列 SpringAOP

概念 原来的方法写法 public void save(){ Long startTime System.currentTimeMillis(); System.out.println(“book dao save ...”); Long endTime System.currentTimeMillis(); Long totalTime endTime-startTime; System.out.prin…

解决Vue3中使用setup如何定义组件的name属性

vue2的书写name: <template><div></div> </template><script> export default {name: "nameoption", }; </script><style lang"scss" scoped></style>解决&#xff1a;Vue3中使用setup如何定义组件的nam…