一、介绍
1) Go 支持面向对象编程特性(并非传统的oop)。
2) Go 是基于 struct 来实现 OOP 特性的,struct等同于其他语言的class
3) Go 去掉了传统 OOP 语言的重载、构造函数和析构函数、隐藏的 this 指针等等
4) Go 面向对象的三大特征继承,封装和多态
二、结构体
type 结构体名称 struct {
field1 type
field2 type
}
三、创建并实例化结构体
// 结构体
type person struct {
Name string
Age int
Sex int8
}
func main() {
// 创建方式1 声明-赋值-使用
var p1 person
fmt.Println(p1) // { 0 0}
p1.Name="小明"
p1.Age=18
p1.Sex=1
fmt.Println(p1) // {小明 18 1}
}
四、结构体在内存的布局
fmt.Println(p1) // { 0 0}
p1.Name="小明"
p1.Age=18
p1.Sex=1
fmt.Println(p1) // {小明 18 1}
初始 赋值之后
Name "" ------------> 小明
var p1 person-----------> Age 0 ------------> 18
Sex 0 ------------> 1
五、结构体字段/属性
1) 结构体字段 /属性/ field
2) 字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。
3) 字段声明语法同变量
4) 结构体未赋值前,会有默认值
布尔类 false ,数值是 0 ,字符串是 “"。
数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0]
指针,slice,和 map 的零值都是 nil ,即还没有分配空间。
5) 结构体是值类型。
// 结构体
type Person struct {
Name string // 基本类型
Age int
Scores [5]float64 // 数组
ptr *int // 引用类型
slice []int
map1 map[string]string
}
func main() {
// 定义结构体变量
var p1 Person
fmt.Println(p1)
// 结构体默认值 { 0 [0 0 0 0 0] <nil> [] map[]}
if p1.ptr == nil {
fmt.Println("ok1")
}
if p1.slice == nil {
fmt.Println("ok2")
}
if p1.map1 == nil {
fmt.Println("ok3")
}
// 使用切片 一定要make
p1.slice = make([]int, 10)
p1.slice[0] = 100
// 使用map 一定要make
p1.map1 = make(map[string]string)
p1.map1["key1"] = "tom~"
}
六、创建结构体变量和访问结构体字段
// 结构体
type Person struct {
Name string
Age int
Scores [5]float64
ptr *int
slice []int
map1 map[string]string
}
func main() {
// 方式一
var p1 Person
p1.Name = "和平"
fmt.Println(p1) // {和平 0 [0 0 0 0 0] <nil> [] map[]}
// 方式二
p2 := Person{Name: "小明", Age: 20}
fmt.Println(p2) // {小明 20 [0 0 0 0 0] <nil> [] map[]}
// 方式三
var p3 *Person = new(Person)
(*p3).Name = "小红"
p3.Age = 20
fmt.Println(p3) // &{小红 20 [0 0 0 0 0] <nil> [] map[]}
// 方式4
var p4 *Person = &Person{}
(*p4).Name = "小猴"
p4.Age = 18
fmt.Println(p4) // {小猴 18 [0 0 0 0 0] <nil> [] map[]}
}
1) 第 3 种和第 4 种方式返回的是 结构体指针。
2) 结构体指针访问字段的标准方式应该是:(*结构体指针).字段名 ,比如 (*person).Name = "tom"
3) 但 go 做了一个简化,也支持 结构体指针.字段名, 比如 person.Name = "tom"。更加符合程序员
使用的习惯,go 编译器底层 对 person.Name 做了转化 (*person)
七、struct类型的内存分配机制
八、结构体使用注意
1) 结构体的所有字段在内存中是连续的
2) 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
package main
import "fmt"
/**
结构体之间的转换
*/
type Student struct {
Age int
}
type Person struct {
Age int
}
func main() {
var s Student = Student{10}
var p Person = Person{ 15}
s = Student(p)
fmt.Println(s) // 15
}
3) 结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
package main
import "fmt"
/**
结构体之间的转换
*/
type Student struct {
Age int
}
type Stu Student
func main() {
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1) // 19
fmt.Println(s2) // 19
}
4) struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。
type Monster struct{
Name string `json:"name"` //`json:"name"就是 struct tag
Age int `json:"age"`
skill string `json:"skill"`
}
func main() {
//1.创建一个Monster变量
monster := Monster{"牛魔王", 500, "芭蕉扇~"}
//2.将monster变量序列化为json格式字串json.Marshal 函数中使用反射
jsonstr,err := json.Marshal(monster)
if err != nil {
fmt.Println("json 处理错误", err)
}
fmt.Println("jsonstr",string(jsonstr))
}
八、方法的声明和调用
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 类型绑定的
// 结构体
type Person struct {
Name strin
}
// Person绑定方法
func (p Person) test() {
fmt.Println("test() name=", p.Name) //test() name= 猴子
}
func main() {
p := Person{Name: "猴子"}
p.test()
}
1) test 方法和 Person 类型绑定
2) test 方法只能通过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调用
3) func (p Person) test() {}... p 表示哪个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非常相似
九、方法的调用和传参机制原理
1) 调用方法和调用函数是一样的
2) 不一样的地方时,通过变量去调用,并传入变量本身,可以是值传递,也可以是引用传递
十、方法的声明和定义
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) 方法可以用在自定义类型上,而不仅仅是 struct, 比如 int , float32 等都可以有方法
4)和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
5) 如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出
type Person struct{
Name string
Age int
}
func (p *Person) String() string {
return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
}
func main(){
p1 := Person {"小明", 18}
fmt.Println(&p1) // Name: 小明, Age: 18
}
十二、方法和函数的区别
1) 调用方式不一样
函数:
函数名(实参列表)
方法:
变量.方法名(实参列表)
2) 对于普通函数,指针类型就只能用&调用
3) 对于方法 可以用(&) 也可以直接调用,具体是值传递 还是地址传递看方法的绑定
2)3)参考以下案例,注意一个是以参数形式传入,一个是绑定形式传入
//对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
func teste1(p Person1) {
fmt.Println(p.Name)
}
func teste2(p *Person1) {
fmt.Println(p.Name)
}
func(p Person1) teste3() {
p.Name = "jack"
fmt.Println("test03()=",p.Name)// jack
}
func(p *Person1)teste4(){
p.Name = "mary"
fmt.Println("teste3()=",p.Name)// mary
}
func main(){
p := Person1{"tom"}
teste1(p) // 值传递
teste2(&p) // 地址传递
fmt.Println("main1=",p.Name) // tom
(&p).teste3() // 里面 name 改成了 jack
fmt.Println("main2=",p.Name) // tom
p.teste3() // 效果一样
p.teste4() // 里面改成了mary
fmt.Println("main3=",p.Name) // mary
(&p).teste4() // 效果一样
// 方法都能用 p 或者 (&p)调用,具体是值传递还是地址传递,看方法的绑定 带*地址传递 不带值传递
}
十三、创建结构体
type Person struct{
Name string
Age int
}
func makeStruct() {
// 1 声明使用
var p Person
p.Name = "小唐"
fmt.Println(p) // {小唐 0}
// 2 直接赋值
p1 := Person{"小猴", 18}
fmt.Println(p1) // {小猴 18}
// 3 直接赋值②
p2 := Person{
Name: "Alice",
}
fmt.Println(p2) // {Alice 0}
// 4 new一个
p3 := new(Person)
p3.Name = "Bob"
p3.Age = 25
fmt.Println(p3) // Name: Bob, Age: 25
fmt.Println(*p3) // {Bob 25}
// 5 &方式
p4 := &Person{
Name: "Charlie",
Age: 35,
}
fmt.Println(p4) // Name: Charlie, Age: 35
fmt.Println(*p4) // {Charlie 35}
// 6 工厂
p5 := NewPerson("David", 40)
fmt.Println(p5) // {David 40}
p6 := NewPerson2("Eve", 28)
fmt.Println(*p6) // {Eve 28}
}
func NewPerson(name string, age int) Person {
return Person{
Name: name,
Age: age,
}
}
func NewPerson2(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
}
}
十四、工厂方法
type Person struct{
Name string
Age int
}
func NewPerson(name string, age int) Person {
return Person{
Name: name,
Age: age,
}
}
func NewPerson2(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
}
}
func makeStruct() {
// 6 工厂
p5 := NewPerson("David", 40)
fmt.Println(p5) // {David 40}
p6 := NewPerson2("Eve", 28)
fmt.Println(*p6) // {Eve 28}
}