Go面向对象说明

  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 中面向接口编程是非常重要的特性。

结构体

结构体是值类型,默认为值拷贝。除非使用指针进行拷贝,为引用,修改指针引用的变量,原结构体也会随之改变

结构体中的指针,切片,映射的默认值均为nil,未分配空间,如果要使用,应首先进行make

声明结构体

type 结构体名称 struct { 
	field1 type
	field2 type
}

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

方法一:直接声明

var person Person

方法二:{ }

var person Person = Person{field,field}

方法三:&

var person *Person = new (Person)

方法四:&、{ }

var person *Person = &Person{field,field}

说明

(*person).Name = "tom"
person.Name = "tom"

使用细节

  1. 结构体的所有字段在内存中是连续
  2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
  3. 结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强制类型转换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyiUMSNi-1650294339716)(C:\Users\X\AppData\Roaming\Typora\typora-user-images\image-20220418174408616.png)]

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

type Person struct{
	Name string `ison:"name"`
	Age int `json:"age"`
}
person := Person{"lzq",18}
//将变量序列化为JSON格式字串
//json.Marshal函数中使用反射
jsonStr,err := json.Marshal(person)
if err != nil{
    fmt.Prinln("json处理错误",err)
}
fmt.Prinln("jsonStr",string(jsonStr))

方法

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

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

方法的声明和调用

type Person struct { 
	Name int
}
func (p Person) test() { 
    fmt.Println("test() name=",p.Name)
}
//调用
func main(){
    var p Person
    p.Name="lzq"
    p.test()//调用方法
}

test( )方法和Person类型绑定

test方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用

var dog Dog
dog.test()
test()//这两种调用方式都是错误的

方法可以传入参数、返回值

func(p Person) calculate(n int) res int

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

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法。

如以下代码

func(p Person) calculate(n int) res int{
	res:= 0
	for i:=1;i<=n;i++{
		res += i
	}
	return res
}
func main(){
    var p Person
    n:=2
    res := calculate(n)
    fmt.Prinln(res)
}

程序运行过程中会先为main函数创建一个栈区

main栈

p—>[lzq] Name

n—>[2]

res:=30

再调用calculate方法并传入实参

calculate栈

p—>[lzq] Name

n—>[2]

return 6

方法的注意事项和细节

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
  2. 修改结构体变量的值,可以通过结构体指针的方式来处理
func (p *Person) modify() {
    p.Name ="xmy"
}//此处的p.Name等价于(*p).Name
var p Person
p.Name ="lzq"

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

//在其他数据类型中使用方法
package main
import (
	"fmt"
)
type integer int
func (i integer) print() {
	fmt.Println("i=",i)
}
func ( i *integer) change(){
	*i+=1
}
func main() {
	var i integer = 10
	i.print()
	i.change()
	fmt.Println("i=",i)
	
}

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

type Student struct{
	Name string
	Age int
}
//给*string实现方法String()
func (stu *Student) String() string{
    str := fmt.Sprintf("Name=[%v] Age=[%v]",stu.Name,stu.Age)
    return str
}
func main(){
    stu := Student{
   		Name : "lzq",
   		Age : 18,
	}
	fmt.Println(&stu)
}

方法和函数的区别

  1. 调用方式不一样

    1. 函数的调用方式: 函数名(实参列表)
    2. 方法的调用方式: 变量.方法名(实参列表)
  2. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然

  3. 方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
    在这里插入图片描述
    在这里插入图片描述
    上图揭示了方法传参的特点。

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