Go (三) 面向对象1

一、面向对象

1.1、初识golang面向对象

golang支持面向对象编程(OOP)。

golang没有类(class),go语言的结构体(struct)和其他编程语言的类(class)有同等的地位。简单理解golang是根据struct来实现面向对象编程(OOP)的。

golang有面向对象的继承封装多态的特性

golang面向对象很优雅,通过(interface )关联,耦合性低,很灵活。也就是说golang中面向接口编程是非常重要的特性。

二、结构体(struct)和结构体变量(值类型)

2.1、结构体(struct)和结构体变量的快速入门(更多方式:详见2.4)

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Cat struct {
	Name string
	Age int
	Color string
}

func main() {
	// 创建一个结构体变量"cat",类型为"Cat",是一个struct
	var cat1 Cat
	fmt.Println(cat1) // 返回的默认值:{ 0 } ,string默认值为"",int默认值为0,所以这里显示只有一个0

	// 给变量的"属性"赋值
	cat1.Color="yellow"
	cat1.Age=2
	cat1.Name="dandan"

	fmt.Println(cat1) // 返回值:{dandan 2 yellow} 分别对应Name,Age,Color的值
	fmt.Println(cat1.Name) // 返回值:dandan
	fmt.Println(cat1.Age) // 返回值:2
	fmt.Println(cat1.Color) // 返回值:yellow
	fmt.Printf("%T",cat1) // 类型:main.Cat
}

2.2、结构体变量在内存中的布局(重要)

1.声明一个结构体变量时,数据结构已经存在了,数据的值就是不同类型的默认值;

2.结构体变量是值类型(相互数据不影响,如果想要相互数据影响的话,详见:2.5)

2.3、结构体( struct**)声明**

type 结构体名称 struct {

field1 type

field2 type

}

2.3.1、结构体( struct**)声明例子**

结构体名称首字母大写,那么结构体可以在其他包被使用

结构体(字段|属性)首字母大写,那么结构体字段可以在其他包被使用

不同结构体变量的字段是独立的,互不影响

type Cat struct {

Name string // (字段|属性)是 string类型

Age int // (字段|属性)是 int类型

Color string // (字段|属性)是 string类型

}

2.3.2、创建一个结构体变量后,如果没有给字段赋值,那么会有一个默认值:

布尔类型的默认值是:false

string类型的默认值是:""

int类型的默认值是:0

指针,slice(切片)和map的默认值都是nil,即没有分配内存空间(在使用时,需要先make)

例子如下:

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
	par1 *int  // 指针
	slice1 []int  // 切片
	map1 map[string]string  // map
}

func main() {
	// 定义结构体变量
	var test Test
	fmt.Println(test.par1) // 默认值:<nil>
	fmt.Println(test.slice1) // 默认值:[]
	fmt.Println(test.map1)  // 默认值:map[]
	if test.par1 == nil {
		fmt.Println("test.par1 is nil")
	}
	if test.slice1 == nil {
		fmt.Println("test.slice1 is nil")
	}
	if test.map1 == nil {
		fmt.Println("test.map1 is nil")
	}
	// 依次输出:
	// test.par1 is nil
	// test.slice1 is nil
	// test.map1 is nil

	// 此时给"结构体变量"直接赋值,一定会报错。需要先给切片make,在使用
	test.slice1=make([]int,2)
	test.slice1[0]=111
	test.slice1[1]=222
	fmt.Println(test.slice1) // [111 222]

	//需要先给map make,在使用
	test.map1=make(map[string]string)
	test.map1["name"]="sudada"
	test.map1["age"]="18"
	fmt.Println(test.map1) // map[age:18 name:sudada]
}

2.4、结构体变量的声明和结构体变量字段使用

2.4.1、方式1(先声明结构体变量,然后赋值)

var test struct

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	var test Test
	test.Name="sudada"
	test.Age=18
	fmt.Println(test) // {sudada 18}
}

2.4.2、方式1的延伸(先声明结构体变量,然后赋值)

var test = struct{}

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	var test = Test{}
    // 简写 test := Test{}
	test.Name="sudada"
	test.Age=18
	fmt.Println(test) // {sudada 18}
}

2.4.3、方式2(声明结构体变量,同时赋值)

var test = struct{Name: "sudada",Age: 18,}

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	var test = Test{
		Name: "sudada",
		Age: 18,
	}
	fmt.Println(test) // {sudada 18}
}

2.4.4、方式3(结构体变量是一个指针时,然后赋值)

var test *struct = new(struct)

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	// 结构体变量是一个"指针"
	var test *Test = new(Test)  // 可以写成:var test = new(Test)
	// 给"指针"类型的"结构体变量"字段赋值的方式(标准写法)
	(*test).Name="sudada"
	(*test).Age=18
	fmt.Println(*test) // {sudada 18}

	// go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18
	test.Name="wang"
	test.Age=28
	fmt.Println(*test) // {wang 28}
}

2.4.5、方式3的延伸(结构体变量是一个指针时,然后赋值)

var test *struct = &struct{}

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	// 结构体变量是一个"指针"
	var test *Test = &Test{}
	// 给"指针"类型的"结构体变量"字段赋值的方式(标准写法)
	(*test).Name="sudada"
	(*test).Age=18
	fmt.Println(*test) // {sudada 18}

	// go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18
	test.Name="wang"
	test.Age=28
	fmt.Println(*test) // {wang 28}
}

2.4.6、方式4(结构体变量是一个指针,同时赋值)

var test *struct = &Test{Name:"sudada", Age:28,}

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	// 结构体变量是一个"指针",声明时赋值
	var test *Test = &Test{
		Name:"sudada",
		Age:28,
	}
	fmt.Println(*test) // {sudada 28}

	// go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18
	test.Name="wang"
	test.Age=28
	fmt.Println(*test) // {wang 28}
}

2.5、结构体变量的将指针赋值给另外一个结构体变量时,内存分配机制

"test2指针"对应的"值"是"test1的指针地址"(那么修改test1或者test2时,2边的值都会跟着改变)

Go 复制代码
// 定义结构体变量,也叫做"对象"
type Test struct {
	Name string
	Age int
}

func main() {
	var test1 = Test{}
	test1.Name="sudada"
	test1.Age=18

	// 把test1的"指针"赋值给test2
	fmt.Printf("%p\n",&test1) // test1的指针是:0xc000008048
	var test2 *Test = &test1
	fmt.Printf("%p\n",&test2) // test2的指针是:0xc00006a028(test2的指针存放的值就是test1的指针)
	fmt.Printf("%p\n",test2) // test2的值(等于test1的指针)是:0xc000008048
	fmt.Println(*test2) // test2:{sudada 18}

	// 修改"指针"的值时,test1和test2对应的值都会改变
	test2.Name="wang"
	fmt.Println(test1)  // test1:{wang 18}
	fmt.Println(*test2)  // test2:{wang 18}
}

2.6、结构体struct 注意事项

2.6.1、结构体和其他类型进行转换时,需要有完全相同的字段(字段名字,字段个数和字段类型)

Go 复制代码
type A struct {
	Name string
	Age int
}

type B struct {
	Name string
	Age int
}

func main() {
	var a A
	var b B
	// 这样强制转换不会报错(A B结构体字段名称,字段个数和字段类型相同)
	a = A(b)
	fmt.Println(a,b)  // { 0} { 0}
}

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

Go 复制代码
// 结构体1
type Student struct {
	Name string
	Age int
}
// 结构体2
type Stu Student

func main() {
	var stu1 Student
	var stu2 Stu
	// 这样强制转换不会报错(Student Stu结构体字段名称,字段个数和字段类型相同)
	stu1 = Student(stu2)
	fmt.Println(stu1,stu2)  // { 0} { 0}
}

2.6.3、结构体的每个字段都可以写一个tag,该tag可以通过反射机制获取

type student struct {
Name string `json:"name"`
Age int `json:"age"`
Sex string `json:"sex"`
}

Go 复制代码
import (
	"encoding/json"
	"fmt"
)

// 结构体1
type student struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Sex string `json:"sex"`
}

func main() {
	var stu1 = student{"sudada",18,"nan",}
	fmt.Println(stu1) // {sudada 18 nan}

	// 把结构体变量stu1序列化(这里传入"结构体字段"必须大写,如果不大写"结构体字段",那么json.Marshal读取不到值)
	jsonStr, err := json.Marshal(stu1)
	if err != nil {
		fmt.Println("报错: ",err)
	} else {
		fmt.Println(string(jsonStr)) // 默认返回的是bytes类型的切片,需要使用string做一下转换
		// 返回值(json格式):{"Name":"sudada","Age":18,"Sex":"nan"}

		// 结构体字段使用tag之后:
		// 返回值(json格式):{"name":"sudada","age":18,"sex":"nan"}
	}
}

三、方法

3.1、什么是方法?

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

3.2、

相关推荐
winks38 分钟前
Spring Task的使用
java·后端·spring
Null箘9 分钟前
从零创建一个 Django 项目
后端·python·django
云空13 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
秋意钟18 分钟前
Spring新版本
java·后端·spring
青莳吖23 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall30 分钟前
期末考学C
java·开发语言
重生之绝世牛码32 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行38 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Algorithm15761 小时前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
shinelord明1 小时前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程