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、

相关推荐
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
励志成为嵌入式工程师2 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
码农小旋风3 小时前
详解K8S--声明式API
后端
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端