golang-struct

essaycode essaycode     2023-04-06     342

关键词:

Golang-struct

  Golang 语言面向对象编程说明

    1)Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。

    2)Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解 Golang 是基于 struct 来实现 OOP 特性的。

    3)Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函数、隐藏的 this 指针等等

    4)Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。

    5)Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang 中面向接口编程是非常重要的特性。

  

  结构体与结构体变量(实例/对象)的关系示意图

    技术图片

    对上图的说明
    1)将一类事物的特性提取出来(比如猫类), 形成一个新的数据类型, 就是一个结构体。
    2)通过这个结构体,我们可以创建多个变量(实例/对象)
    3)事物可以猫类,也可以是 Person , Fish 或是某个工具类。。。

      技术图片

 

 

   

  快速入门-面向对象的方式(struct)解决养猫问题
  代码演示

    技术图片 技术图片

 

 

  

  结构体和结构体变量(实例)的区别和联系 通过上面的案例和讲解我们可以看出:

    1)结构体是自定义的数据类型,代表一类事物.

    2)结构体变量(实例)是具体的,实际的,代表一个具体变量

 

  结构体变量(实例)在内存的布局(重要!)

    技术图片

 

 

   

  如何声明结构体
    基本语法
      type 结构体名称 struct
        field1 type field2 type
      

    举例:
      type Student struct
        Name string //字段Age int //字段Score float32
      

    字段/属性基本介绍

      1)从概念或叫法上看: 结构体字段 = 属性 = field (即授课中,统一叫字段)
      2)字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如我们前面定义猫结构体 的 Name string 就是属性?注意事项和细节说明
      1)字段声明语法同变量,示例:字段名 字段类型
      2)字段的类型可以为:基本类型、数组或引用类型
      3)在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样: 布尔类型是 false ,数值是 0 ,字符串是 ""。 数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0] 指针,slice,和 map 的零值都是 nil ,即还没有分配空间。案例
      演示:

        技术图片 技术图片

 

 

       4)不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个, 结构体是值类型。

        技术图片 技术图片

 

 

         画出上面代码的内存示意图:

          技术图片

 

 

     

  创建结构体变量和访问结构体字段

    方式 1-直接声明
      案例演示: var person Person

    方式 2-
      案例演示: var person Person = Person

       技术图片

    方式 3-&
      案例: var person *Person = new (Person)

      技术图片

    方式 4-
      案例: var person *Person = &Person

       技术图片

      说明:
      1)第 3 种和第 4 种方式返回的是 结构体指针。
      2)结构体指针访问字段的标准方式应该是:(*结构体指针).字段名 ,比如 (*person).Name = "tom"
      3)但 go 做了一个简化,也支持 结构体指针.字段名, 比如 person.Name = "tom"。更加符合程序员使用的习惯,go 编译器底层 对 person.Name 做了转化 (*person).Name。

  struct 类型的内存分配机制
    看一个思考题

    技术图片

 

 

     输出的结果是: p2.Name = tom p1.Name = 小明

    基本说明

      技术图片

 

    结构体在内存中示意图

      技术图片

 

 

     看下面代码,并分析原因

       技术图片

      输出的结果是

      技术图片

 

 

       分析代码

      技术图片

 

 

   结构体使用注意事项和细节

    1)结构体的所有字段在内存中是连续的

      技术图片 技术图片

    2)结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
      技术图片

 

 

     3)结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转

      技术图片

 

 

     4)struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。  

      技术图片

 

 

       案例

       技术图片 技术图片

 

 

   

  方法
    基本介绍
      在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名..),Person 结构体还有一些行为比如:可以说话、跑步..,通过学习,还可以做算术题。这时就要用方法才能完成。

      Golang 中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct。
    方法的声明和调用
      type A struct Num int
      
      func (a A) test()

        fmt.Println(a.Num)
      
      对上面的语法的说明
        1)func (a A) test() 表示 A 结构体有一方法,方法名为 test
        2)(a A) 体现 test 方法是和 A 类型绑定的
          举例说明

           技术图片

          1)test 方法和 Person 类型绑定
          2)test 方法只能通过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调用  
          技术图片

        3)func (p Person) test() ... p 表示哪个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非常相似。

        4)p 这个名字,有程序员指定,不是固定, 比如修改成 person 也是可以

          技术图片

 

 

     

  方法快速入门

    1)给 Person 结构体添加 speak 方法,输出 xxx 是一个好人

 

 

       技术图片

 

 

     2)给 Person 结构体添加 jisuan 方法,可以计算从 1+..+1000 的结果, 说明方法体内可以函数一样, 进行各种运算

      技术图片

 

 

     3)给 Person 结构体 jisuan2  方法,该方法可以接收一个数 n,计算从 1+..+n  的结果

 

      技术图片

 

 

     4)给 Person 结构体添加 getSum 方法,可以计算两个数的和,并返回结果

      技术图片

 

 

     5)方法的调用

      技术图片

 

 

   

  方法的调用和传参机制原理:(重要!)

    说明:
    方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法。下面我们举例说明。

    案例 1:
      画出前面 getSum 方法的执行过程+说明

 

       技术图片

      说明:
      1)在通过一个变量去调用方法时,其调用机制和函数一样
      2)不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地质拷贝)
    案例 2
      请编写一个程序,要求如下:
      1)声明一个结构体 Circle, 字段为 radius
      2)声明一个方法 area 和 Circle 绑定,可以返回面积。
      3)提示:画出 area 执行过程+说明

         技术图片

 

 

   

  方法的声明(定义)

    func (recevier type) methodName(参数列表) (返回值列表)
      方法体
      return 返回值
    

    1)参数列表:表示方法输入
    2)recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型
    3)receiver type : type 可以是结构体,也可以其它的自定义类型
    4)receiver : 就是 type 类型的一个变量(实例),比如 :Person 结构体 的一个变量(实例)
    5)返回值列表:表示返回的值,可以多个
    6)方法主体:表示为了实现某一功能代码块
    7)return 语句不是必须的。

  方法的注意事项和细节
    1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
    2)如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理

      技术图片

 

 

     3)Golang 中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct, 比如 int , float32 等都可以有方法

      技术图片

    4)方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。[讲解]
    5)如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出

      技术图片 技术图片

  方法的课堂练习题
    1)编写结构体(MethodUtils),编程一个方法,方法不需要参数,在方法中打印一个 10*8 的矩形, 在 main 方法中调用该方法。

    2)编写一个方法,提供 m 和 n 两个参数,方法中打印一个 m*n 的矩形

      技术图片 

      技术图片

       技术图片

    3)编写一个方法算该矩形的面积(可以接收长 len,和宽 width), 将其作为方法返回值。在 main方法中调用该方法,接收返回的面积值并打印。
      技术图片

 

     4)编写方法:判断一个数是奇数还是偶数

       技术图片

    5)根据行、列、字符打印 对应行数和列数的字符,比如:行:3,列:2,字符*,则打印相应的效果
      技术图片

    6)定义小小计算器结构体(Calcuator),实现加减乘除四个功能实现形式 1:分四个方法完成:实现形式 2:用一个方法搞定
      技术图片 技术图片

 

  方法的课后练习题

    技术图片

  方法和函数区别
    1)调用方式不一样
      函数的调用方式: 函数名(实参列表)
      方法的调用方式: 变量.方法名(实参列表)

    2)对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然

       技术图片 技术图片

 

     3)对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以

      技术图片 技术图片

      总结:
      1)不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定.
      2)如果是和值类型,比如 (p Person) , 则是值拷贝, 如果和指针类型,比如是 (p *Person) 则是地址拷贝。

面向对象编程应用实例

  1)声明(定义)结构体,确定结构体名
  2)编写结构体的字段
  3)编写结构体的方法

  
  学生案例:
    1)编写一个 Student 结构体,包含 name、gender、age、id、score 字段,分别为 string、string、int、int、float64 类型。
    2)结构体中声明一个 say 方法,返回 string 类型,方法返回信息中包含所有字段值。
    3)在 main 方法中,创建 Student 结构体实例(变量),并访问 say 方法,并将调用结果打印输出。
    4)走代码

package main
import "fmt"
	
// 学生案例:
// 编写一个 Student 结构体,包含 name、gender、age、id、score 字段,分别为 string、string、int、int、float64 类型。
// 结构体中声明一个 say 方法,返回 string 类型,方法返回信息中包含所有字段值。
// 在 main 方法中,创建 Student 结构体实例(变量),并访问 say 方法,并将调用结果打印输出。

type Student struct  
	name string 
	gender string 
	age int
	id int
	score float64

	
func (student *Student) say() string 
	infoStr := fmt.Sprintf("student 的信息 name=[%v] gender=[%v], age=[%v] id=[%v] score=[%v]", student.name, student.gender, student.age, student.id, student.score)
	return infoStr

func main() 
	//测试
	//创建一个 Student 实例变量
	var stu = Student name : "tom", gender : "male", age : 18,
	id : 1000,
	score : 99.98,
	
	fmt.Println(stu.say())

  

 

创建结构体变量时指定字段值
  方式一
    技术图片 技术图片

 

   方式二

    技术图片