【GO语言卵细胞级别教程】11

  1. 结构体定义使用type
package mystudy
//结构体知识点
import "fmt"
// 定义一个人结构体
type person struct{
	name string
	age int
	school string
}

  1. 创建结构体的四种方式
func DemoStruct(){
	fmt.Println("------结构体------")
	// 声明结构体变量
	var p1 person
	p1.name = "张三"
	p1.age = 19
	p1.school = "清华大学"
	fmt.Println(p1)
	// 声明方式2
	var p2 person = person{
		name:"里斯",
		age:18,
		school:"理工大学",
	}
	fmt.Println(p2)
	// 声明方式3
	var p3 = person{
		name:"王五",
		age:21,
		school:"武汉大学",
	}
	fmt.Println(p3)
	// 声明方式4
	var p4 = person{"王五",22,"武汉大学"}
	fmt.Println(p4)
}
//输出结果
------结构体------
{张三 19 清华大学}
{里斯 18 理工大学}
{王五 21 武汉大学}
{王五 22 武汉大学}

1.2.2结构体方法
  1. 结构体方法
    结构体方法(Struct Methods)是一种与结构体类型(Struct Type)关联的函数。在 Go 语言中,结构体方法是在结构体类型上定义的函数,可以通过结构体对象调用。
  2. 语法结构
func (接收者类型) 方法名(参数列表) 返回值 {
    // 方法的实现
}
// 例如
type Persion struct{
  name string
}
func(p Persion)test(num int)(bool){

}

其中,接收者类型指的是与结构体方法关联的结构体类型。在方法定义中,接收者出现在方法名之前,并用括号括起来。参数列表和返回值与普通函数的定义方式相同。
3. 实战

package mystudy
// 面向对象 结构体之方法
import "fmt"

type Persion struct{
	name string
	age int
}

func(p Persion) persionInfo(){
	message := fmt.Sprintf("persion info:name:%v,age:%v",
		p.name, p.age)
	fmt.Println(message)
}
// 使用指针赋值 因为结构体函数是个
func (p \*Persion) set\_name(name string)(bool){
	(\*p).name = name
	// 底层编译器
	fmt.Println("名字修改成功:", name)
	return true
}

func DemoObjStruct(){
	fmt.Println("------结构体之方法------")
	var p = Persion{
		name:"张三",
		age:19,
	}
	p.persionInfo()
	p.set\_name("韩信")
	p.persionInfo()
}

以上方法可以看到,实际没有传入指针时,这个结构体方法中是值传递,也就是其他语言说的形参,形参改变不改变原有的数据,所以为了达到set_name 可以设置p.name的效果,这里使用了p.name='xx’来进行设置

1.2.3结构体方法绑定指定类型

在 Go 语言中,我们可以使用接收者(Receiver)来绑定方法。接收者可以是结构体类型或非结构体类型,可以是指针类型或非指针类型。

接收者出现在函数名之前,并用括号括起来。它可以是任何类型,可以是用户自定义的类型,也可以是内置类型。

在方法定义中,接收者的类型决定了该方法与哪种类型关联。接收者可以是值类型(非指针类型)或指针类型。如果接收者是值类型,则方法操作的是接收者的副本,而如果接收者是指针类型,则方法可以修改接收者本身。

以下是几种常见的接收者类型及其绑定方法的示例:

  1. 值类型接收者:
type Rectangle struct {
    width  float64
    height float64
}

func (r Rectangle) Area() float64 {
    return r.width \* r.height
}

  1. 指针类型接收者:
type Counter struct {
    count int
}

func (c \*Counter) Increment() {
    c.count++
}

  1. 内置类型的接收者:
type MyInt int

func (m MyInt) IsPositive() bool {
    return m > 0
}

在上述示例中,我们分别使用值类型和指针类型接收者定义了结构体类型 RectangleCounter 的方法。另外,还使用内置类型 MyInt 定义了一个方法。

要调用绑定的方法,我们需要创建相应的对象,并通过对象调用方法。

rect := Rectangle{width: 5, height: 3}
area := rect.Area()

counter := &Counter{}
counter.Increment()

num := MyInt(10)
isPositive := num.IsPositive()

在上述示例中,我们通过创建对象 rectcounternum,然后通过对象调用相应的方法来使用绑定的方法。
4. 使用int绑定方法

package mystudy
// 面向对象 结构体之方法
import "fmt"

type Integer int

func (i Integer)absoluteValue()(Integer){
	fmt.Println("修改之前:", i)
	if i < 0 {
		i = -i
	}
	fmt.Println("修改之后:", i)
	return i
}

func DemoObjStruct(){
	fmt.Println("------方法扩展------")
	var num Integer
	num = -100
	num1 := num.absoluteValue()
	fmt.Println(num)
	fmt.Println(num1)
}
// 输出结果
修改之前: -100
修改之后: 100
-100
100

1.2.4方法访问控制

结构体绑定的方法和函数一样首字母大写的话可以在本包的其他包访问,搜测只能在本包访问

1.2.5方法如果定义了String函数
  1. 先看代码
package mystudy
// 面向对象 结构体之方法
import "fmt"

type Integer int

func (i Integer)absoluteValue()(Integer){
	fmt.Println("修改之前:", i)
	if i < 0 {
		i = -i
	}
	fmt.Println("修改之后:", i)
	return i
}

func(i \*Integer)String() string{

	fmt.Println("===Str===")
	fmt.Println(\*i)
	fmt.Println("===End===")
	return "诸葛"
}


func DemoObjStruct(){
	fmt.Println("------方法扩展------")
	var num Integer
	num = -100
	num1 := num.absoluteValue()
	fmt.Println(&num)
	fmt.Println(&num1)
	var nint int
	fmt.Println(&nint)
}
// 输出结果
------方法扩展------
修改之前: -100
修改之后: 100
===Str===
-100
===End===
诸葛
===Str===
100
===End===
诸葛

可以看到定义了一个方法String() 这个时候当我们调用fmt.Println(&num)传递一个地址的时候,这个时候会调用我们自定义的打印函数,注意一下几个条件,才可以触发我们自定义的函数String

  1. 定义类型 type mytype xxx
  2. 绑定函数 func (x *xxx)String() string{}
  3. 定义变量 var xx mytype
  4. 调用fmt.Println(&xx)传入参数 然后就会触发String函数

1.3GO语言的方法和函数

在 Go 语言中,方法(Method)和函数(Function)有以下区别:

  1. 定义方式:方法是与特定类型关联的函数,因此它们在类型的定义中声明。函数是独立的代码块,可以在任何地方定义。
  2. 接收者:方法具有一个额外的参数,称为接收者(Receiver),它定义了方法与哪个类型相关联。接收者可以是结构体类型或非结构体类型,可以是值类型或指针类型。函数没有接收者。
  3. 调用方式:方法通过类型的对象或指针进行调用,使用点运算符(.)来访问方法。函数直接通过函数名进行调用,不需要使用点运算符。
  4. 修改能力:如果方法的接收者是指针类型,它可以修改接收者本身。这是因为指针接收者传递的是地址,可以对原始对象进行修改。函数不能直接修改参数,因为传递给函数的是参数的副本。
  5. 绑定方式:方法是与类型绑定的,因此它们可以访问类型的属性和方法。函数是独立的,不能直接访问类型的属性和方法,除非将类型的对象作为参数传递给函数。

演示了方法和函数的区别:

package main

import "fmt"

type Rectangle struct {
    width  float64
    height float64
}

// 方法:计算矩形的面积
func (r Rectangle) Area() float64 {
    return r.width \* r.height
}

// 函数:计算两个数的和
func Add(a, b int) int {
    return a + b
}

func main() {
    rect := Rectangle{width: 5, height: 3}
    area := rect.Area()
    fmt.Println("矩形的面积:", area)

    sum := Add(2, 3)
    fmt.Println("两个数的和:", sum)
}

方法在调用上也是可以多种方式,可以使用&num 也可以直接使用num,底层都会自动获取,所以这里比较灵活

func (i Integer)absoluteValue()(Integer){
	fmt.Println("修改之前:", i)
	if i < 0 {
		i = -i
	}
	fmt.Println("修改之后:", i)
	return i
}
// 结构体调用方法
fmt.Println("------结构体调用方法------")
fmt.Println((num).absoluteValue())
fmt.Println((&num).absoluteValue())

在上述示例中,Area 是一个方法,它与结构体类型 Rectangle 关联。通过创建 Rectangle 对象 rect,我们可以使用点运算符调用 Area 方法来计算矩形的面积。

Add 是一个函数,它接受两个整数作为参数,并返回它们的和。函数可以直接通过函数名进行调用,并传递参数。

方法可以访问与其关联的类型的属性和方法,而函数则不具备该能力。

总结:方法是与类型关联的函数,具有接收者并可以修改接收者,而函数是独立的代码块,不具有接收者,并且不能直接访问类型的属性和方法。

1.4结构体声明方式

  1. 直接赋值
// 1.直接赋值方式,但顺序不能变
var a1 Stu  = Stu{"张三", 19}
fmt.Println(a1)

  1. 使用key赋值

// 2.使用key赋值:注意最后一个结尾要加逗号
var a2 Stu = Stu{
	Name:"谢思肖",
	Age:21,
}
fmt.Println(a2)

  1. 指针声明的两种方式

// 3.指针声明
var a3 \*Stu = &Stu{"龙丹丹", 18}
fmt.Println(a3, \*a3)

  1. 指针声明使用key赋值

var a4 \*Stu = &Stu{
	Name:"谢思肖",
	Age:21,
}
fmt.Println(a4, \*a4)

  1. 完整的代码
package mystudy
// 面向对象 结构体之方法
import "fmt"
type Stu struct{
	Name string
	Age int
}

func DemoObjStruct(){
	fmt.Println("------结构体声明方法------")
	// 1.直接赋值方式,但顺序不能变
	var a1 Stu  = Stu{"张三", 19} 
	fmt.Println(a1)
	// 2.使用key赋值:注意最后一个结尾要加逗号
	var a2 Stu = Stu{
		Name:"谢思肖",
		Age:21,
	}
	fmt.Println(a2)
	// 3.指针声明
	var a3 \*Stu = &Stu{"龙丹丹", 18}
	fmt.Println(a3, \*a3)
	var a4 \*Stu = &Stu{
		Name:"谢思肖",
		Age:21,
	}
	fmt.Println(a4, \*a4)
}

1.5使用包导入结构体

不同包之间引用

  1. 首选要大写结构体
  2. 在调用的函数中导入包
文件:stu.go
package mystudy
// 这里要大写大写
type Stu struct{
  name string
  age int
}
/// 调用

文件main.go
package main
import "模块名称/mystudy"
func main(){
    var st mystudy.Stu = mystudy.Stu{"lisi", 22}
}

方案2:针对小写的结构体如何对外使用呢,这里借助一个桥梁,就是对外开放的函数
在这里插入图片描述

  1. 首先定义结构体
  2. 定义一个外部可以访问的函数
  3. 访问函数以修改结构体
    定义 mystudy.go
package mystudy
// 面向对象 结构体之方法
import "fmt"


type teacher struct{
	Name string
	age int
}

func InitTeacher(name string, age int)\*teacher{
	return &teacher{name, age}
}

func DemoObjStruct(){
	var te \*teacher
	te = &teacher{"wagn", 22}
	fmt.Println(te)
	(\*te).Name = "www"
	fmt.Println(te)
}

定义主函数

package main

import (
	"fmt"
	"com.gdzs/goproject/src/com/gdzs/mystudy"
)
func main(){
	te := mystudy.InitTeacher("青青", 28)
	fmt.Println(te)
	(\*te).Name = "qingqing"
	// 也可以写成 te.Name = "qingqing"
	fmt.Println(te)
}

// 输出结果
&{青青 28}
&{qingqing 28}

注意

  1. 以上的teacher的属性需要首字母大写,主函数中才可以使用te.Name修改,如果是小写则无法访问,就会报如下错误
main.go:86:8: (\*te).name undefined (type mystudy.teacher has no field or method name)

  1. 注意声明的时候,不能使用var xxx mystudy.teacher,因为这个结构体是不对外使用的,所以只能使用xxx:=xx这种隐式声明类型的方式。

1.6结构体嵌入

1.6.1 嵌入方式1:匿名结构体

结构体嵌入(Struct embedding)是一种在一个结构体中嵌入另一个结构体的方式,以便通过嵌入的结构体来访问其字段和方法。

通过结构体嵌入,可以将一个结构体类型作为另一个结构体的字段,使得被嵌入的结构体的字段和方法成为了外部结构体的一部分,就好像它们是直接定义在外部结构体中一样。

使用结构体嵌入的主要目的是实现代码的重用和组合。通过嵌入结构体,可以将通用的字段和方法提取到一个单独的结构体中,并在其他结构体中嵌入该结构体,从而实现代码的复用,并且可以通过外部结构体访问嵌入结构体的字段和方法。

package mystudy
// 结构体嵌入知识点
import "fmt"
type Animal struct{
	Name string
	Age int
}

func getAnimal(name string, age int)\*Animal{
	return &Animal{name, age}
}


func(a Animal) eat(){
	fmt.Println(a.Name, "吃东西")
}


type Dog struct{
	Animal // 嵌入结构体
	weight int 
}

func (d Dog) call(){
	fmt.Println(d.Name,"叫了一声,说她的体重是:",d.weight)
}
func (d \*Dog) setWight(weight int){
	(\*d).weight = weight
	fmt.Println(d.weight)
}

func DemoQianru(){
	// 声明方式1
	var d Dog = Dog{Animal{"花花", 22}, 108}
	fmt.Println(d)
	// 声明方式2
	// 在 Go 语言中,结构体的字段初始化可以按照顺序进行赋值。
	// 但是在嵌套结构体中,如果嵌入的结构体没有指定字段名,
	// 那么在初始化时不能省略字段名。
	var d2 Dog = Dog{Animal:Animal{"兰兰", 22}, weight:108}
	fmt.Println(d2)
	d.setWight(99)
	fmt.Println(d)
	// 声明方式3使用指针
	var d3 \*Dog = &Dog{Animal:Animal{"雨雨", 22}, weight:108}
	(\*d3).setWight(98)
	fmt.Println((\*d3))
}

嵌入的结构体可以通过变量直接调用,如上代码所示

// 访问方式1
d2.weight
// 访问方式2
d2.Animal.Name

对于指针,这几种方式调用都是可以使得

对于指针,这几种方式调用都是可以使得s
fmt.Println(d3.Animal.Name)
fmt.Println(d3.Name)
fmt.Println((\*d3).Name)

1.7.2结构体嵌入:当作一个字段
  1. 一个结构体当作一个类型来嵌入结构体中
type Dog struct {  
	a Animal // Animal结构体被嵌入到Dog结构体中 
}  

  1. 声明和调用方法
    1. 声明:var c1 = cat{Animal{“猫猫”, 19}, 180}
    2. 调用方式:c1.a.XXX
package mystudy
// 结构体嵌入知识点
import "fmt"
type Animal struct{
	Name string
	Age int
}

func getAnimal(name string, age int)\*Animal{
	return &Animal{name, age}
}

func(a Animal) eat(){
	fmt.Println(a.Name, "吃东西")
}

type cat struct{
	a Animal
	high int 
}


func DemoQianru(){
	fmt.Println("------结构体嵌入,当作类型------")
	// 声明
	var c1 = cat{Animal{"猫猫", 19}, 180}
	fmt.Println(c1)
	// 此语句无法调用,会报错
	// 16.结构体嵌入.go:59:17: c1.Name undefined (type cat has no field or method Name)
	// fmt.Println(c1.Name)
	// 使用以下带调用
	fmt.Println(c1.a.Name)
}

通过以上如果想通过类来实现的话,还得是使用嵌入的匿名结构体,因为它会自动搜索结构体中的属性的。不需要指定具体的属性

1.7面向对象-封装

有了1.6章节的嵌入结构体可以看到要是实现封装的话,使用嵌入匿名的结构体可以完成。

1.7.1给她创建一个类吧

在 Go 语言中,没有类的概念,而是使用结构体(struct)和方法(method)来实现面向对象的编程。
可以通过定义一个结构体来表示一个类型,并在该结构体上定义方法来操作和处理该类型的实例。这种结构体和方法的组合可以用来模拟类的概念。

下面是一个创建结构体和方法的示例:
(1)首字母大写,其他包可以访问,否则,只能在当前包可以访问
(2)属性也是一样的,大写首字母可以对外访问,否则只能当前包可以访问
(3)方法也是如此:方法使用结构体绑定方法

  1. 定义一个类
package mystudy
// 类知识
type Student struct{
     Name string
     age int
}


  1. 创建构造函数:因为结构体都是值传递,所以这里使用指针
func getStudent(name string, age int)\*Student{
	return &Student(name, age)
}

  1. 创建方法:使用结构体绑定方法
    注意这里设置需要传入指针,才能改变对应的值
func (s \*Student)setName(name string){
	(\*s).Nmae = name
}

func (s Student)getName(){
    return s.Name
}

1.7.2类的继承

类继承:使用嵌入匿名结构体即可
(1)实现父类结构体,实现子类结构体,子类结构体嵌入匿名父类结构体
(2)同样定义了showInfo()方法 必须参数和返回值都一样,不能出现参数不同,返回值不同的同名函数,否则会报错
(3)当调用的时候会先在子类中寻找方法或者属性,如果找不到再调用父类的

// 定义父类结构体
type People struct{
    Name string
    Age int
}
// 定义父类方法
func (p People)showInfo()string{
	fmt.Println("父类方法showInfo")
    message := fmt.Sprinf("我的名字:%v 我的年龄",p.Name, p.Age)
    return message
}
// 定义父类方法
func (p People)whoami(){
	fmt.Println("我是谁:", p.Name)
}
-------------------------------
// 定义子类结构体
type Student struct{
	People
	notes string
}
// 定义子类发方法
func(s Student) showInfo()string{
	fmt.Println("子类方法showInfo")
    message := fmt.Sprinf("我的名字:%v 我的年龄",s.Name, s.Age)
    return message
}


完整的测试代码

package mystudy
// 面向对象-继承
import "fmt"

// 定义父类结构体
type People struct{
    Name string
    Age int
}
func (p \*People)setName(name string){
	(\*p).Name = name
}

// 定义父类方法
func (p People)showInfo()string{
	fmt.Println("父类方法showInfo")
    message := fmt.Sprintf("我的名字:%v 我的年龄:%v",p.Name, p.Age)
    return message
}
// 定义父类方法
func (p People)whoami(){
	fmt.Println("我是谁:", p.Name)
}

// 定义子类结构体
type Student struct{
	People
	notes string
}
// 定义子类发方法
func(s Student) showInfo()(message string){
	// 子类的方法如果和父类同名,那么就必须参数和返回值都是一样的,否则就会报错
	fmt.Println("子类方法showInfo")
    message = fmt.Sprintf("我的名字:%v 我的年龄:%v", s.Name, s.Age)
	fmt.Println(message)
	return 
}

func DemoJc(){
	// 声明一个对象
	fmt.Println("------面向对象-继承------")
	var s1 = Student{People{"关关", 19}, "前任2"}
	message := s1.showInfo()
	fmt.Println(message)
	fmt.Println("==重新设置新的名字:盈盈")
	s1.setName("颖颖")
	message = s1.showInfo()
	fmt.Println(message)
}
// 输出结果
------面向对象-继承------
子类方法showInfo
我的名字:关关 我的年龄:19
我的名字:关关 我的年龄:19
==重新设置新的名字:盈盈
子类方法showInfo
我的名字:颖颖 我的年龄:19
我的名字:颖颖 我的年龄:19

1.7.3多继承

多继承就是按照1.7.2的介绍,这个时候多加几个嵌入结构体就行了

type People struct{
    Name string
    Age int
}

type People2 struct{
    Name string
    Age int
    higi int
}

type Student struct{
     People 
     People2
     notes string
}

func DemoMain(){
    var st = Student{People{"xxx", 19}, People2{"xxx", 21, 180}, "测试"}
}


完整代码

package mystudy
// 面向对象-继承
import "fmt"

/\*
人父类
\*/
// 定义父类结构体
type People struct{
    Name string
    Age int
}

func (p \*People)setName(name string){
	(\*p).Name = name


![img](https://img-blog.csdnimg.cn/img_convert/20005a53b77ffb9134cbd2368b661bce.png)
![img](https://img-blog.csdnimg.cn/img_convert/b6ac500dd1feac37f8613f066c0e74ae.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
ntln("==重新设置新的名字:盈盈")
	s1.setName("颖颖")
	message = s1.showInfo()
	fmt.Println(message)
}
// 输出结果
------面向对象-继承------
子类方法showInfo
我的名字:关关 我的年龄:19
我的名字:关关 我的年龄:19
==重新设置新的名字:盈盈
子类方法showInfo
我的名字:颖颖 我的年龄:19
我的名字:颖颖 我的年龄:19

1.7.3多继承

多继承就是按照1.7.2的介绍,这个时候多加几个嵌入结构体就行了

type People struct{
    Name string
    Age int
}

type People2 struct{
    Name string
    Age int
    higi int
}

type Student struct{
     People 
     People2
     notes string
}

func DemoMain(){
    var st = Student{People{"xxx", 19}, People2{"xxx", 21, 180}, "测试"}
}


完整代码

package mystudy
// 面向对象-继承
import "fmt"

/\*
人父类
\*/
// 定义父类结构体
type People struct{
    Name string
    Age int
}

func (p \*People)setName(name string){
	(\*p).Name = name


[外链图片转存中...(img-cKdnlc4Z-1726122092217)]
[外链图片转存中...(img-diTmQi3J-1726122092218)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值