面向对象

面向对象

结构体

package main

import "fmt"

//定义一个cat的结构体,将cat的属性信息放到cat结构体中进行管理
type Cat struct {
	Name string
	Age int
	Color string
}
func main() {
	//创建一个cat变量
	var cat1 Cat
	cat1.Name = "小白"
	cat1.Age = 10
	cat1.Color = "白色"
	fmt.Println(cat1)

}

结构体的声明和使用

赋值的四种方式

	p2 := Person{"mary",20}
	fmt.Println(p2)
	//方式3
	var p3 *Person= new(Person)
	//因为p3是一个指针,因此标准的字段赋值方式
	(*p3).Name = "jack"
	(*p3).Age = 10
	fmt.Println(*p3)
	//方式4
	var p4 *Person = &Person{"marry",10}
	fmt.Println(*p4)

如果结构体的字段类型是指针,slice和map的值都是nil,如果使用这样的字段,需要先make才能使用

package main

import "fmt"

type Person struct {
	Name string
	Age int
	Score [5]float64
	ptr *int
	slice []int
	map1 map[string]string
}
func main() {

	//如果结构体的字段类型是指针,slice和map的值都是nil,如果使用这样的字段,需要先make才能使用
	//定义一个结构体变量
	var  p1 Person
	fmt.Println(p1)
	// 不同结构体变量的字段是独立,互不影响的,一个结构体变量字段的更改
	//不会影响另外一个,结构体是值类型
	p1.map1 = make(map[string]string)
	p1.map1["key1"] = "tom"
	fmt.Println(p1)



}

package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func main() {
	var p1 Person
	p1.Name = "小明"
	p1.Age = 10
	var p2 *Person = &p1
	fmt.Println((*p2).Age)
	fmt.Println(p2.Age)
	p2.Name = "tom"
	fmt.Printf("p2.Name=%v p1.Name=%v \n", p2.Name, p1.Name)
	fmt.Printf("p2.Name=%v p1.Name=%v \n", (*p2).Name, p1.Name)
	fmt.Printf("p1的地址=%p \n", &p1)
	fmt.Printf("p2的地址=%p p2的值=%p\n", &p2,p2)

}
//10
10
p2.Name=tom p1.Name=tom 
p2.Name=tom p1.Name=tom 
p1的地址=0xc000004480 
p2的地址=0xc000006028 p2的值=0xc000004480

p2里面的值指向p1的内存地址,共享一个地址

使用结构体注意事项

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

    package main
    
    import "fmt"
    
    type Point struct {
    	x int
    	y int
    }
    
    type Rect struct {
    	leftUp, rightDown Point
    }
    
    func main() {
    	r1 := Rect{Point{1, 2}, Point{3, 4}}
    	fmt.Printf("r1.leftUp.x 地址= %p,r1.leftUp.y 地址=%p,r1.rightDown.x 地址=%p,r1.rightDown.y  地址=%p", &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
    }
    //r1.leftUp.x 地址= 0xc00000e420,r1.leftUp.y 地址=0xc00000e428,r1.rightDown.x 地址=0xc00000e430,r1.rightDown.y 地址=0xc00000e438
    
    
  • 结构体是用户单独定义的类型,和其他类型进行转换是需要有完全相同的字段

    package main
    
    import "fmt"
    
    type A struct {
    	Num int
    }
    type B struct {
    	Num int
    }
    
    func main() {
    	var a A
    	var b B
    	a = A(b)
    	fmt.Println(a,b)
    }
    
    
  • 结构体进行type重新定义,go认为是新的数据类型,但是可以相互强制转化

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

    package main
    
    import "fmt"
    import "encoding/json"
    type Monsters struct {
    	Name string `json:"name"` # 这个就是tag
    	Age int `json:"age"`
    	Skill string `json:"skill"`
    }
    
    func main() {
    	//创建一个Monster变量
    	monster :=Monsters{"牛魔王",200,"火云拳"}
    	//将monster变量序列化为 字符串的json
    	jsonStr,err := json.Marshal(monster)
    	if err != nil{
    		fmt.Println(err)
    	}
    	fmt.Println(string(jsonStr))
    }
    
    

方法

go中的方法是作用在指定的数据类型上的,因此自定义类型都可以有方法,不仅仅是struct

type A struct {

	Num int
}
func (a A) test(){
	fmt.Println(a.Num)
}

  • func(a A)test(){}表示A结构体有一个方法,方法名为test

  • (a A)体现test方法是和A类型绑定的

package main
  
  import "fmt"
  
  type AA struct {
  
  	Num int
  }
  func (a AA) test(){
  	fmt.Println(a.Num)
  }
  func main() {
  	a :=AA{1}
  	a.test() //调用方法
  }

  • test方法和AA类型绑定
  • test方法只能通过AA类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用
  • func(a A)test(){}p表示哪个A变量调用,p就是他的副本,和传参类似

案例

给Person结构体添加是计算方法,计算1+...+1000结果

package main

import "fmt"

type Person struct {
	Name string
}

func (person Person) sum() {
	res := 0
	for i := 1; i <= 1000; i++ {
		res += i

	}
	fmt.Println(person.Name, "计算结果是", res)

}
func (person Person) speak() {
	fmt.Println(person.Name, "是一个好人")
}
func main() {
	person := Person{"tom"}
	person.speak()
	person.sum()

}

接收参数

func (person Person) sum(n int) 

有返回值

func (person Person)getSum(n1 int, n2 int) int{
	return n1+n2
}
func main() {
	res :=person.getSum(10,20)
	fmt.Println(res)

}

计算圆的面积

package main

import (
	"fmt"
)

type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	return 3.14 * c.radius * c.radius
}
func main() {
	c := Circle{4.0}
	res := c.area()
	fmt.Println(res)
}

  • 结构体类型是值方法,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

  • 修改结构体变量的值,可以通过指针的方式来处理, 这样就不会是值拷贝类型了

    //
    //func (c Circle) area() float64 {
    //	return 3.14 * c.radius * c.radius
    //}
    ////为了提高效率,我们方法和结构体的指针类型绑定
    //
    //func (c *Circle) area2() float64 {
    //	//因为c是指针,因此我们标准的访问其他字段方式是(*c).radius
    //	//return 3.14 * (*c).radius * (*c).radius
    //	return 3.14 * c.radius * c.radius
    //}
    //func main() {
    //	c := Circle{4.0}
    //	res := c.area()
    //	fmt.Println(res)
    //	//res2 := (&c).area2()
    //	res2 := c.area2()  //解释器在底层做了一个优化
    //	fmt.Println(res2)
    //}
    
    
  • go中的方法作用在指定的数据类型上,因此自定义类型,都可以有方法,不仅仅是struct,如int都可以有方法

  • 方法的访问范围控制的规则,和函数一样.方法名首字母小写,只能在本包中去访问,首字母大写,可以在其他和本包中使用

  • 如果一个变量实现string这个方法,那么fmt.println默认会调用这个变量的string进行输出

练习

  • 编写结构体,编写一个方法,方法不需要参数,在方法中打印一个10*8的长方形

    package main
    
    import "fmt"
    
    type MethodUtils struct {
    }
    
    func (mu MethodUtils) Print() {
    	for i := 1; i <= 10; i++ {
    
    		for j := 1; j <= 8;j++{
    			fmt.Print("*")
    		}
    		fmt.Println()
    	
    }
    func main() {
    	var mu MethodUtils
    	mu.Print()
    }
    
    
  • 编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩形

    
    func (mu MethodUtils) Print2(m int,n int) {
    	for i := 1; i <= m; i++ {
    
    		for j := 1; j <= n;j++{
    			fmt.Print("*")
    		}
    		fmt.Println()
    	}
    
    }
    

方法和函数的区别

  • 对于普通函数,接收者为值类型,不能将指针类型的数据直接传递,反之亦然
  • 不管调用的形式如何,真正决定是只拷贝还是地址拷贝,看这个方法和哪个类型绑定
  • 如果是和值类型,比如(p Person),就是值拷贝,如果是(p *Person)就是地址拷贝
package main

import "fmt"

type Student struct {
   Name   string
   Gender string
   Age    int
   Id     int
   Score  float64
}

func (student *Student) say() string {
   infoStr := fmt.Sprintf("学生的信息 name = [%v] gender = [%v] age = [%v] id = [%v] "+
   	"score = [%v]", student.Name, student.Gender, student.Age, student.Id, student.Score)
   return infoStr
}

func main() {
   stu1 := Student{"zc","男",11,1111,99.09}

   fmt.Println(stu1.say())

}

工厂模式

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

package main

type Student struct {
  Name string...
}
func main() {
  
  
}

因为这里的Student 的字母S是大写的, 如果我们想在其他包创建Student 的实例引入model包后,就可以直接创建Student结构体的变量,如果首字母是小写,就不行了,这个可以用工厂模式来解决

#factory.go
package main

import (
	"demo/model"
	"fmt"
)
func main() {
	stu :=model.NewStudent("tom",10.01)
	fmt.Println(*stu)
	fmt.Println(stu.GetScore())

}
#model/model.go
package model


type student struct {
	Name string
	Score float64
}

func NewStudent(n string,s float64) *student{
	return &student{
		Name: n,
		Score: s,
	}
}

//如果字段小写,可以写一个方法

func (s *student) GetScore() float64{
	return s.score
}

posted @ 2021-01-20 21:54  小子,你摊上事了  阅读(43)  评论(0编辑  收藏  举报