• Golang 面向对象


    1. 简介

    • go 没有面向对象语法等的要求
    • go 语言对于面向对象的设计非常简洁而优雅
    • 没有封装(让数据更加安全,例如年龄不能是负的)、继承(减少代码冗余,父类和子类)、多态(可以产出不同的实例)这些概念,但同样通过别的方式实现这些特性
      • 封装:通过方法实现
      • 继承:通过匿名字段实现
      • 多态:通过接口实现

    2. 匿名字段

    go 结构体中支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段

    package main
    
    import "fmt"
    
    // 定义一个存储人的结构体
    type Person struct {
    	name string
    	sex  int
    	age  int
    }
    
    // 定义一个存储学生的结构体,因为学生是是具有人结构体中的字段特性
    // 不需要重新声明,直接继承人这个结构体的字段就可以
    // 不能存在多个相同的匿名字段
    type Student struct {
    	Person // 	继承人结构体, 匿名字段
    	id      int
    	address string
    }
    
    func main() {
    	// 初始化
    	stu01 := Student{Person{"zs", 0, 18}, 1, "bj"}
    	fmt.Println(stu01)
    
    	stu02 := Student{id: 1, address: "bj"}
    	fmt.Println(stu02)
    
    	stu03 := Student{Person: Person{name:"ls", sex:0, age:1}}
    	fmt.Println(stu03)
    
    	// 取值
    	// 取Person的name (如果子结构体没有和父结构体同名字段,以下两种方式都可以)
    	fmt.Println(stu01.name)
    	fmt.Println(stu01.Person.name)
    
    	fmt.Println(stu01.id)
    }
    

    同名字段的情况

    package main
    
    import "fmt"
    
    // 定义一个存储人的结构体
    type Person struct {
    	name string
    	sex  int
    	age  int
    }
    
    // 定义一个存储学生的结构体,因为学生是是具有人结构体中的字段特性
    // 不需要重新声明,直接继承人这个结构体的字段就可以
    type Student struct {
    	Person // 	继承人结构体, 匿名字段
    	id      int
    	address string
    
    	// 同名字段
    	name string
    }
    
    func main() {
    	var stu Student
    	// 给自己的name字段赋值
    	stu.name = "zs"
    
    	// 给父的name字段进行赋值
    	stu.Person.name = "ls"
    
    	// 取自己的name字段的值(就近原则,不会取到父的name)
    	fmt.Println(stu.name)
    
    	// 取父的name字段的值
    	fmt.Println(stu.Person.name)
    }
    

    所有的内置类型和自定义类型都是可以作为匿名字段去使用

    package main
    
    import "fmt"
    
    // 自定义一个类型
    type myint int
    
    type student struct {
    	int		// 内置类型匿名字段
    	myint	// 自定义类型匿名字段
    }
    
    func main() {
    	// 初始化
    	s := student{1,2}
    
    	// 取值
    	fmt.Println(s.int)
    
    	fmt.Println(s.myint)
    }
    

    指针类型匿名字段

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age int
    	sex int
    }
    
    type Student struct {
    	*Person
    	id int
    	addr string
    }
    
    func main() {
    	stu := Student{&Person{"zs", 18, 1}, 3, "bj"}
    	fmt.Println(stu)
    	fmt.Println(stu.name)
    	fmt.Println(stu.Person.name)
    	fmt.Println(stu.id)
    }
    

    3. 方法

    • 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数
    • 这种带有接收者的函数,我们称为方法,本质上,一个方法则是一个和特殊类型关联的函数
    • 方法的语法如下 func (接收参数名 接收类型) 方法名(参数列表)(返回值)
    • 可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法

    基础类型作为接收者

    package main
    
    import "fmt"
    
    type myint int
    
    // 面向过程思维写法
    func add(a, b myint) myint {
    	return a + b
    }
    
    // 面向对象接口思维写法
    func (a myint) add(b myint) myint {
    	return a + b
    }
    
    func main() {
    	var a myint = 1
    	var b myint = 2
    
    	// 面向过程
    	c := add(a,b)
    	fmt.Println(c)
    
    	// 面向思维
    	d := a.add(b)
    	fmt.Println(d)
    }
    

    结构体作为接收者

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    	sex  int
    }
    
    func (p Person) print() {
    	fmt.Println(p.name, p.age, p.sex)
    }
    
    func main() {
    	p := Person{"zs", 18, -0}
    	p.print()
    }
    

    值语义和引用语义

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    	sex  int
    }
    
    // 引用语义
    func (p *Person) setValue001() {
    	p.name = "123"
    }
    
    // 值语义
    func (p Person) setValue002() {
    	p.name = "abc"
    }
    
    func main() {
    	p := Person{"zs", 18, 0}
    	fmt.Println(p)
    
    	// 值语义, 不会改变原结构体
    	p.setValue002()
    	fmt.Println("值语义",p)
    
    	// 引用语义,会改变原结构体
    	p.setValue001()
    	fmt.Println("引用语义",p)
    }
    

    方法继承

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    	sex  int
    }
    
    type Student struct {
    	Person
    	id   int
    	addr string
    }
    
    func (p *Person) printInfo() {
    	fmt.Println("person", p.name, p.age, p.sex)
    }
    
    func main() {
    	p := Person{"zs", 18, 1}
    	p.printInfo()
    
    	s := Student{Person{"ls", 22, 0}, 1, "bj"}
    	// 子调用父继承来的方法
    	s.printInfo()
    }
    

    方法重写

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    	sex  int
    }
    
    func (p *Person) printInfo() {
    	fmt.Println("person", p.name, p.age, p.sex)
    }
    
    type Student struct {
    	Person
    	id   int
    	addr string
    }
    
    // 重写父的printInfo方法
    func (s *Student) printInfo() {
    	fmt.Println("student", s.name, s.age, s.sex, s.id, s.addr)
    }
    
    func main() {
    	p := Person{"zs", 18, 1}
    	p.printInfo()
    
    	s := Student{Person{"ls", 22, 0}, 1, "bj"}
    	// 子调用重写父来的方法
    	s.printInfo()
    }
    

    方法值和方法表达式

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    	sex  int
    }
    
    func (p *Person) printInfo() {
    	fmt.Println("person", p.name, p.age, p.sex)
    }
    
    func main() {
    	p := Person{"zs", 18, 1}
    	p.printInfo()
    
    	// 方法值
    	pFunc01 := p.printInfo
    	pFunc01()
    
    	// 方法表达式
    	pFunc02 := (*Person).printInfo
    	pFunc02(&p)
    }
    

    1. 包和封装

    • 方法首写字母大写可以被外部包调用
    • 方法首写字母小写是私有方法只能在当前包内调用
    • 为结构体定义的方法必须放在同一个包内,可以是不同的文件

    5. init 函数以及执行顺序

    一个包里可以有 0 个或多个 init 函数,在程序启动时自动调用

    package main
    
    import "fmt"
    
    var a int = 10
    var b int = 20
    
    func init() {
    	fmt.Println("a: ", a)
    	fmt.Println("init01")
    }
    
    func init() {
    	fmt.Println("b: ", b)
    	fmt.Println("init02")
    }
    
    func main() {
    	fmt.Println("main")
    }
    

    go 程序初始化顺序

    main包 --> import --> 全局const --> 全局var --> init()  --> main()
    

    如果一个 main 包引入了别的包,初始化顺序是先初始化被引用的包

    6. 接口

    • go 语言中,接口(interface)是一个自定义类型,描述了一系列方法的集合

    • 接口不能被实例化

    • 接口定义语法 type 接口名 interface{}

      • ps: 接口命名习惯以er结尾

    接口的定义与实现

    package main
    
    import "fmt"
    
    // 定义一次接口
    type Humaner interface {
    	// 定义接口的方法
    	Say()
    }
    
    // 定义学生信息结构体
    type Student struct {
    	name  string
    	age   int
    	score float64
    }
    
    // Student结构体实现Humaner接口
    func (stu *Student) Say() {
    	fmt.Println("学生...: ", stu.name)
    }
    
    type Teacher struct {
    	name    string
    	age     int
    	subject string
    }
    
    func (tea *Teacher) Say() {
    	fmt.Println("老师...: ", tea.name)
    }
    
    type myString string
    
    func (mystr myString) Say() {
    	fmt.Println("myString...: ", mystr)
    }
    
    // 定义一个接收Humaner接口类型参数的统一函数
    func WhoSay(s Humaner) {
    	s.Say()
    }
    
    func main() {
    	stu01 := &Student{"zs", 18, 99.8}
    	tea01 := &Teacher{"ls", 20, "计算机"}
    	var mystr myString = "hello"
    
    	// 各自调用自己的方法
    	stu01.Say()
    	tea01.Say()
    	mystr.Say()
    
    	// 使用统一接口调用
    	WhoSay(stu01)
    	WhoSay(tea01)
    	WhoSay(mystr)
    
    	// 已经实现接口的类型也可以是那个接口类型
    	x := make([]Humaner, 3)
    	x[0], x[1], x[2] = stu01, tea01, mystr
    	for _, v := range x {
    		v.Say()
    	}
    }
    

    接口继承

    package main
    
    import "fmt"
    
    // 定义一次接口
    type Humaner interface {
    	// 定义接口的方法
    	Say()
    }
    
    // 定义继承Humaner的接口
    type Personer interface {
    	// 相当于写了Say()
    	Humaner
    	Sing(lyrics string)
    }
    
    // 定义学生信息结构体
    type Student struct {
    	name  string
    	age   int
    	score float64
    }
    
    // Student结构体实现Humaner接口
    func (stu *Student) Say() {
    	fmt.Println("学生...: ", stu.name)
    }
    
    func (stu *Student) Sing(lyrics string) {
    	fmt.Println(lyrics)
    }
    
    
    // 定义一个接收Humaner接口类型参数的统一函数
    func WhoSay(s Humaner) {
    	s.Say()
    }
    
    func main() {
    	stu01 := &Student{"zs", 18, 99.8}
    	var p Personer
    	// 结构体对象赋值给接口对象
    	p = stu01
    	// 接口调用方法
    	p.Say()
    	p.Sing("hello world")
    }
    

    接口类型变量

    可以存储任何实现了该接口所有方法的对象类型

    package main
    
    import "fmt"
    
    type Animal interface {
    	Talk()
    	Eat()
    	Name() string
    }
    
    type Dog struct {
    	name string
    }
    
    func (d *Dog) Talk() {
    	fmt.Println("汪汪汪...")
    }
    
    func (d *Dog) Eat() {
    	fmt.Println("吃狗x")
    }
    
    func (d *Dog) Name() string {
    	fmt.Println(d.name)
    	return d.name
    }
    
    type Pig struct {
    	name string
    }
    
    func (p *Pig) Talk() {
    	fmt.Println("哼哼哼...")
    }
    
    func (p *Pig) Eat() {
    	fmt.Println("吃猪x")
    }
    
    func (p *Pig) Name() string {
    	fmt.Println(p.name)
    	return p.name
    }
    
    func main() {
        // 创建两个实现了Animal 接口的对象
    	dog01 := &Dog{"旺财"}
    	pid01 := &Pig{"佩奇"}
    
        // 定义接口类型变量
    	var a Animal
    
        // 可以直接将实现了Animal 接口的对象赋值给Animal接口类型
    	a = dog01
    	a.Eat()
    	a.Name()
    	a.Talk()
    
    	a = pid01
    	a.Eat()
    	a.Name()
    	a.Talk()
    }
    

    接口类型和指针类型

    值类型实现接口,指针类型可以存进去; 但指针类型实现接口,值类型存不进去

    值类型实现接口,指针类型可以存进去

    package main
    
    import "fmt"
    
    type Animal interface {
    	Talk()
    	Eat()
    	Name() string
    }
    
    type Dog struct {
    	name string
    }
    
    func (d Dog) Talk() {
    	fmt.Println("汪汪汪...")
    }
    
    func (d Dog) Eat() {
    	fmt.Println("吃狗x")
    }
    
    func (d Dog) Name() string {
    	fmt.Println(d.name)
    	return d.name
    }
    
    func main() {
    	var dog01 *Dog = &Dog{"旺财"}
    
    	var a Animal
    
    	a = dog01
    	a.Eat()
    	a.Name()
    	a.Talk()
    }
    

    指针类型实现接口,值类型存不进去

    package main
    
    import "fmt"
    
    type Animal interface {
    	Talk()
    	Eat()
    	Name() string
    }
    
    type Dog struct {
    	name string
    }
    
    func (d *Dog) Talk() {
    	fmt.Println("汪汪汪...")
    }
    
    func (d *Dog) Eat() {
    	fmt.Println("吃狗x")
    }
    
    func (d *Dog) Name() string {
    	fmt.Println(d.name)
    	return d.name
    }
    
    func main() {
    	var dog01 Dog
    	var a Animal
    
    	// 若类变量存储在接口变量中
    	// 若传值类型,不能获取变量地址,d取不到地址
    	// 寻址问题不通过
    	// 编译不通过
    	a = dog01
    }
    

    同一个类型可以实现多个接口

    package main
    
    import "fmt"
    
    type Animal interface {
    	Talk()
    	Eat()
    	Name() string
    }
    
    type Animal2 interface {
    	Run()
    }
    
    type Dog struct {
    	name string
    }
    
    func (d *Dog) Talk() {
    	fmt.Println("汪汪汪...")
    }
    
    func (d *Dog) Eat() {
    	fmt.Println("吃狗x")
    }
    
    func (d *Dog) Name() string {
    	fmt.Println(d.name)
    	return d.name
    }
    
    // Dog结构体实现第二个接口
    func (d *Dog) Run() {
    	fmt.Println("run")
    }
    
    func main() {
    	dog01 := &Dog{}
    	var a01 Animal
    	var a02 Animal2
    	a01 = dog01
    	a02 = dog01
    
    	a01.Talk()
    	a02.Run()
    }
    

    接口是可以嵌套的

    package main
    
    type Animal interface {
    	Talk()
    	Eat()
    	Name() string
    }
    
    type Animal2 interface {
    	Run()
    }
    
    // 继承前两个接口
    type Animal3 interface {
    	Animal
    	Animal2
    }
    
    func main() {
    }
    

    空接口

    args ...interface{}

    func Println(a ...interface{}) (n int, err error) {
    	return Fprintln(os.Stdout, a...)
    }
    

    类型查询

    • comma-ok 断言
    • switch测试

    comma-ok 断言

    package main
    
    //comma-ok断言
    
    import "fmt"
    
    type Person struct {
    	name string
    	age int
    }
    
    func main() {
    	// 定义空接口切片
    	t := make([]interface{}, 3)
    	t[0] = 1
    	t[1] = "hello"
    	t[2] = &Person{}
    
    	// 类型断言: value,ok := 元素.(Type)
    	// value是变量值,ok是布尔,是不是这个类型
    	for i, v := range t {
    		if value, ok := v.(int); ok {
    			fmt.Printf("index[%d]是int类型, value: %d
    ", i, value)
    		}else if value, ok := v.(string); ok {
    			fmt.Printf("index[%d]是string类型, value: %s
    ", i, value)
    		} else {
    			fmt.Printf("其他类型
    ")
    		}
    	}
    }
    

    switch 测试

    package main
    
    import "fmt"
    
    type Person struct {
    	name string
    	age  int
    }
    
    func main() {
    	// 定义空接口切片
    	t := make([]interface{}, 3)
    	t[0] = 1
    	t[1] = "hello"
    	t[2] = &Person{}
    
    	for i, v := range t {
    		switch value := v.(type) {
    		case int:
    			fmt.Printf("index[%d]是int类型, value: %d
    ", i, value)
    		case string:
    			fmt.Printf("index[%d]是string类型, value: %s
    ", i, value)
    		default:
    			fmt.Printf("其他类型
    ")
    		}
    	}
    }
    
  • 相关阅读:
    50个提高PHP程序运行效率的方法
    虚拟主机FTP上传文件为什么要用二进制上传
    Status Bar 总结
    TableView 总结
    阿里Java开发手册(泰山版)个人记录
    下载excel模板
    微信公众号-发送模板消息
    ffmpeg获取视频时长
    微信公众号授权
    根据word模板生成word、转换成pdf、打成war包
  • 原文地址:https://www.cnblogs.com/zhichaoma/p/12510004.html
Copyright © 2020-2023  润新知