Go的面向对象(1)| 豆包MarsCode AI 刷题

44 阅读5分钟

Go的面向对象

go的面向对象编程和传统的有区别,go只是支持面向对象编程特性,相对简洁,去掉了传统oop的extends,方法重载,构造函数和折构函数,this等

go仍然有面向对象编程的继承,封装,多态特性,只是实现方式同传统面向对象不同

结构体

只有结构体实例化的时候才分配内存

Go语言可以通过多种方式实例化结构体,根据实际需要可以选用不同的写法。

第一种

var ins T  // 通过声明来实例化

第二种

ins := new(T)  // 通过内置函数new,这里ins的类型是*T属于指针
ins.Name = "张三"  // 可以用.给结构体字段赋值,C语言通过指针访问成员变量只能用 ->,这是Go的语法糖

第三种

ins := &T{} // 在Go语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作
ins := T{}  //这里ins类型是T

初始化结构体

ins := 结构体类型名{
    字段1: 字段1的值,
    字段2: 字段2的值,
    …
}
ins := 结构体类型名{
    字段1的值,
    字段2的值,
    …
}
ins := &结构体类型名{
    字段1: 字段1的值,
    字段2: 字段2的值,
    …
}
// 这时的ins就是地址了

匿名结构体

匿名结构体没有名称,无需type关键字可以直接使用

ins := struct {
    // 匿名结构体字段定义
    字段1 字段类型1
    字段2 字段类型2
    …
}{
    // 字段值初始化
    初始化字段1: 字段1的值,
    初始化字段2: 字段2的值,
    …
}

结构体细节

  • 结构体名称是直接指向实际结构体实例内存空间的,并没有指向一个地址这个地址再指向实际内存,所以go中的结构体是值类型的,默认值拷贝

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

  • 结构体之间可以类型转换但是结构体字段要完全一样

  • struct字段上可以写一个tag,该tag可以通过反射机制获取,常见与序列化和反序列化

    • // Cat 结构体字段名大写是为了让别的包能访问到这个字段,
      // 比如json序列化结构体,如果字段名小写,那么json包不能访问就无法序列化
      type Cat struct {
      	Name  string `json:"name"` // struct tag
      	Age   int    `json:"age"`
      	Color string `json:"color"`
      }
      

方法

  • golang的方法是和数据类型绑定的,自定义类型都可以有方法
  • 在通过一个变量调用方法时,其调用机制和函数一样
  • 不一样的地方是变量调用方法时,该变量本身也会作为一个参数传递到方法 ,这个变量是值类型,那么传递到这个方法时就会进行值拷贝,一般对于结构体我们使用指针类型变量传递,避免了值拷贝,提高效率
func (a *A) Test() {
    fmt.Println(a.num)
}
  • 如果一个类型实现了String()这个方法,那么fmt.Println默认调用这个变量的String进行输出

和函数的区别:

  • 调用方式不一样
    • 函数:函数名(实参列表)
    • 方法:变量.方法名(实参列表)
  • 函数的接收者为值类型就只能传值类型,是指针类型就只能传指针类型
  • 方法接收者为值类型,也可以直接用指针类型的变量调用方法,反之亦然
    • 需要注意的是,如果方法接受者为值类型,我们用指针类型变量调用,形式上是传入地址,本质依然是值拷贝,简单来说就是方法接收者是什么类型,调用是就是什么拷贝,和调用它的变量的类型无关

工厂模式

golang结构体没有构造函数,通常使用工厂模式来解决这个问题

如果结构体名字首字母小写,我们还想在其他包中创建该结构体实例,就可以使用类似于其他语言中的构造函数来解决

func newCommand(name string, varref *int, comment string) *Command {
    return &Command{
        Name:    name,
        Var:     varref,
        Comment: comment,
    }
}

cmd = newCommand(
    "version",
    &version,
    "show version",
)

继承

为了解决代码复用,减少冗余

golang中没有extends,实现继承的效果是通过嵌入匿名结构体

type Goods struct {
    Name  string
    Price float64
}

type Book struct {
    Goods
    Author string
}

type TV struct {
	*Goods
}
var b Book
  • 结构体可以使用嵌套匿名结构体所有字段和方法,不管大写还是小写
  • 匿名结构体字段访问可以简化b.Goods.Name —> b.Name (编译器会先看Book中有没有Name字段,如果有直接调用Book的,没有就去看Book中嵌入的匿名结构体)
  • 当结构体和它嵌入的匿名结构体有相同的字段,访问时遵循就近原则,如果希望访问匿名结构体中的,可以通过匿名结构体名来区分
  • 如果嵌入多个匿名结构体,其中有相同的字段或方法,在访问时就必须指定匿名结构体名,否则编译报错
  • 如果嵌入的是有名结构体,访问其中字段必须带上嵌入结构体名(嵌入有名结构体实现的效果就不是继承了,只是简单组合)

嵌入匿名字段也是允许的


真亲切……