Go入门(五)之面向对象编程、异常处理

上一篇文章> Go入门(四)之工程管理、复合类型

一、面向对象编程


1、匿名组合

封装:通过方法实现
继承:通过匿名字段实现
多态:通过接口实现

(1)匿名字段初始化
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	Person //只有类型,没有名字,匿名字段
	id     int
	addr   string
}

func main() {
	// 顺序初始化
	var s1 Student = Student{Person{"wielun", 'm', 18}, 1, "bj"}
	fmt.Println("s1 = ", s1)

	// 自动推导类型
	s2 := Student{Person{"wielun", 'm', 18}, 1, "bj"}
	// %+v,显示更详细
	fmt.Printf("s2 = %+v\n", s2)

	// 制定成员初始化
	s3 := Student{Person: Person{name: "wielun"}, id: 1}
	fmt.Printf("s3 = %+v\n", s3)
}

结果:

s1 =  {{wielun 109 18} 1 bj}
s2 = {Person:{name:wielun sex:109 age:18} id:1 addr:bj}
s3 = {Person:{name:wielun sex:0 age:0} id:1 addr:}
(2)操作成员
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	Person //只有类型,没有名字,匿名字段
	id     int
	addr   string
}

func main() {
	s1 := Student{Person{"wielun", 'm', 18}, 1, "bj"}
	fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)
	s1.name = "haha"
	s1.sex = 'f'
	s1.age = 22
	s1.id = 666
	s1.addr = "cd"
	fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)
	s1.Person = Person{"go", 'm', 18}
	fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)
}

结果:

wielun 109 18 1 bj
haha 102 22 666 cd
go 109 18 666 cd
(3)同名字段
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	Person
	id   int
	addr string
	name string // 和Person同名
}

func main() {
	// 声明(定义一个变量)
	var s Student
	// 就近原则
	s.name = "mike"
	s.sex = 'm'
	s.age = 18
	s.addr = "bj"

	// 显式调用
	s.Person.name = "wielun"

	fmt.Printf("s = %+v\n", s)
}

结果:

s = {Person:{name:wielun sex:109 age:18} id:0 addr:bj name:mike}
(4)非结构体匿名字段
package main

import "fmt"

type mystr string //自定义类型,给一个类型改名

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	Person //结构体匿名字段
	int    //基础类型的匿名字段
	mystr
}

func main() {
	s := Student{Person{"mike", 'm', 18}, 666, "hahaha"}
	fmt.Printf("s = %+v\n", s)
	fmt.Println(s.name, s.age, s.sex, s.int, s.mystr)
	fmt.Println(s.Person, s.int, s.mystr)
}

结果:

s = {Person:{name:mike sex:109 age:18} int:666 mystr:hahaha}
mike 18 109 666 hahaha
{mike 109 18} 666 hahaha
(5)结构体指针类型匿名字段
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	*Person //指针类型
	id      int
	addr    string
}

func main() {
	s1 := Student{&Person{"mike", 'm', 18}, 666, "bj"}
	fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)

	// 先定义变量
	var s2 Student
	s2.Person = new(Person) //分配空间
	s2.name = "wielun"
	s2.sex = 'm'
	s2.age = 18
	s2.id = 222
	s2.addr = "cd"
	fmt.Println(s2.name, s2.sex, s2.age, s2.id, s2.addr)
}

结果:

mike 109 18 666 bj
wielun 109 18 222 cd

2、方法

(1)面向过程和对象函数的区别

面向对象只是换了一种表现形式

package main

import "fmt"

// 面向过程
func Add01(a, b int) int {
	return a + b
}

// 面向对象,方法:给某个类型绑定一个函数
type long int

// tmp叫接收者,接收者就是传递的一个参数
func (tmp long) Add02(other long) long {
	return tmp + other
}

func main() {
	var result int
	result = Add01(1, 1) // 普通函数调用方式
	fmt.Println("result = ", result)

	// 定义一个变量
	var a long = 2
	// 调用方法格式: 变量名.函数(所需参数)
	r := a.Add02(3)
	fmt.Println("r = ", r)
}

结果:

result =  2
r =  5
(2)为结构体类型添加方法
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

// 带有接收者的函数叫方法
func (tmp Person) PrintInfo() {
	fmt.Println("tmp = ", tmp)
}

// 通过一个函数,给成员赋值
func (p *Person) SetInfo(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

func main() {
	// 定义同时初始化
	p := Person{"mike", 'm', 18}
	p.PrintInfo()

	// 定义一个结构体变量
	var p2 Person
	(&p2).SetInfo("wielun", 'f', 22)
	p2.PrintInfo()
}

结果:

tmp =  {mike 109 18}
tmp =  {wielun 102 22}
(3)值语义和引用语义
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

// 修改成员变量的值
// 参数为普通变量,非指针,值语义,一份拷贝
func (p Person) SetInfoVal(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
	fmt.Println("p = ", p)
	fmt.Printf("SetInfoVal &p = %p\n", &p)
}

// 接收者为指针变量,引用传递
func (p *Person) SetInfoPointer(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
	fmt.Printf("SetInfoPointer p = %p\n", p)
}

func main() {
	s1 := Person{"go", 'm', 22}
	fmt.Printf("&s1 = %p\n", &s1)

	// 值语义
	s1.SetInfoVal("mike", 'm', 18)
	fmt.Println("s1 = ", s1)

	// 引用语义
	(&s1).SetInfoPointer("mike1", 'f', 20)
	fmt.Println("s1 = ", s1)
}

结果:

&s1 = 0xc000114440
p =  {mike 109 18}
SetInfoVal &p = 0xc000114480
s1 =  {go 109 22}
SetInfoPointer p = 0xc000114440
s1 =  {mike1 102 20}
(4)方法集

用实例value和pointer调用方法(含匿名字段)不受方法集约束,编译器总是查找全部方法,并自动抓换receiver实参。

<1> 指针变量的方法集

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Println("SetInfoValue")
}

func (p *Person) SetInfoPointer() {
	fmt.Println("SetInfoPointer")
}

func main() {
	// 结构体变量是一个指针变量,它能够调用那些方法,这些方法就是一个集合,简称方法集
	p := &Person{"wielu", 'm', 18}
	p.SetInfoPointer()

	// 内部做的转换,先把指针p,转成*p后再调用
	// (*p).SetInfoValue()
	p.SetInfoValue()
}

结果:

SetInfoPointer
SetInfoValue

<2> 普通变量的方法集

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Println("SetInfoValue")
}

func (p *Person) SetInfoPointer() {
	fmt.Println("SetInfoPointer")
}

func main() {
	p := Person{"wielu", 'm', 18}
	p.SetInfoPointer()
	// 内部先把p转换为&p在调用

	p.SetInfoValue()
}

结果:

SetInfoPointer
SetInfoValue
(5)方法的继承
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

// Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
	fmt.Printf("name = %s, sex = %c, age = %d\n", tmp.name, tmp.sex, tmp.age)
}

// 有一个学生,继承Person字段,成员和方法都继承
type Student struct {
	Person
	id   int
	addr string
}

func main() {
	s := Student{Person{"wielun", 'm', 18}, 666, "cd"}
	s.PrintInfo()
}

结果:

name = wielun, sex = m, age = 18
(6)方法的重写
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

// Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
	fmt.Printf("name = %s, sex = %c, age = %d\n", tmp.name, tmp.sex, tmp.age)
}

// 有一个学生,继承Person字段,成员和方法都继承
type Student struct {
	Person
	id   int
	addr string
}

// 与Person同名,叫重写
func (tmp *Student) PrintInfo() {
	fmt.Println("Student: tmp = ", tmp)
}

func main() {
	s := Student{Person{"wielun", 'm', 18}, 666, "cd"}

	// 就近原则
	s.PrintInfo()

	// 显式调用继承方法
	s.Person.PrintInfo()
}

结果:

Student: tmp =  &{{wielun 109 18} 666 cd}
name = wielun, sex = m, age = 18
(7)方法值
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Printf("SetInfoValue: %p, %v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
	fmt.Printf("SetInfoPointer: %p, %v\n", p, p)
}

func main() {
	p := Person{"wielun", 'm', 18}
	fmt.Printf("main: %p, %v\n", &p, p)

	p.SetInfoPointer()
	// 保存方式入口地址
	pFunc := p.SetInfoPointer //这个就是方法值,调用函数时,无需再传递接收者
	pFunc()

	vFunc := p.SetInfoValue
	vFunc()
}

结果:

main: 0xc0000044a0, {wielun 109 18}
SetInfoPointer: 0xc0000044a0, &{wielun 109 18}
SetInfoPointer: 0xc0000044a0, &{wielun 109 18}
SetInfoValue: 0xc000004540, {wielun 109 18}
(8)方法表达式
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Printf("SetInfoValue: %p, %v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
	fmt.Printf("SetInfoPointer: %p, %v\n", p, p)
}

func main() {
	p := Person{"wielun", 'm', 18}
	fmt.Printf("main: %p, %v\n", &p, p)

	// 方法值
	f := (*Person).SetInfoPointer
	f(&p) //显式把接收者传递过去

	f2 := (Person).SetInfoValue
	f2(p)
}

结果:

main: 0xc000096440, {wielun 109 18}
SetInfoPointer: 0xc000096440, &{wielun 109 18}
SetInfoValue: 0xc0000964c0, {wielun 109 18}

3、接口

(1)概述

在这里插入图片描述

(2)接口的定义和实现
package main

import "fmt"

type Humaner interface {
	// 方法只有声明,没有实现,由别的类型(自定义类型)实现
	sayhi()
}

// Student实现了此方法
type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

// Teacher实现了此方法
type Teacher struct {
	addr  string
	group int
}

func (tmp *Teacher) sayhi() {
	fmt.Printf("Teacher[%s, %d] sayhi\n", tmp.addr, tmp.group)
}

type MyStr string

// MyStr实现了此方法
func (tmp *MyStr) sayhi() {
	fmt.Printf("MyStr[%s] sayhi\n", *tmp)
}

func main() {
	// 定义接口类型的变量
	var i Humaner
	// 只要实现了此接口方法的类型,那么这个类型的变量(接收者类型)就可以给i赋值
	s := &Student{"mike", 666}
	i = s
	i.sayhi()

	t := &Teacher{"bj", 12}
	i = t
	i.sayhi()

	var str MyStr = "hello wielun"
	i = &str
	i.sayhi()
}

结果:

Student[mike, 666] sayhi
Teacher[bj, 12] sayhi
MyStr[hello wielun] sayhi
(3)多态的表现
package main

import "fmt"

type Humaner interface {
	// 方法只有声明,没有实现,由别的类型(自定义类型)实现
	sayhi()
}

// Student实现了此方法
type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

// Teacher实现了此方法
type Teacher struct {
	addr  string
	group int
}

func (tmp *Teacher) sayhi() {
	fmt.Printf("Teacher[%s, %d] sayhi\n", tmp.addr, tmp.group)
}

type MyStr string

// MyStr实现了此方法
func (tmp *MyStr) sayhi() {
	fmt.Printf("MyStr[%s] sayhi\n", *tmp)
}

// 定义一个普通函数,函数的参数为接口类型
// 只有一个函数,可以有不同表现,多态
func WhoSayHi(i Humaner) {
	i.sayhi()
}

func main() {
	s := &Student{"mike", 666}
	t := &Teacher{"bj", 12}
	var str MyStr = "hello wielun"
	WhoSayHi(s)
	WhoSayHi(t)
	WhoSayHi(&str)

	fmt.Printf("------\n")
	// 创建一个切片
	x := make([]Humaner, 3)
	x[0] = &str
	x[1] = t
	x[2] = &str

	for _, i := range x {
		i.sayhi()
	}
}

结果:

Student[mike, 666] sayhi
Teacher[bj, 12] sayhi
MyStr[hello wielun] sayhi
------
MyStr[hello wielun] sayhi
Teacher[bj, 12] sayhi
MyStr[hello wielun] sayhi
(4)接口的继承
package main

import "fmt"

type Humaner interface {  //子集
	sayhi()
}

type Personer interface {  //超集
	Humaner //匿名字段,继承了sayhi
	sing(lrc string)
}

// Student实现了此方法
type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
	fmt.Println("Stuent sing:", lrc)
}

func main() {
	// 定义一个变量
	var i Personer
	s := &Student{"wielun", 666}
	i = s
	i.sayhi() //继承过来的方法
	i.sing("绿光")
}

结果:

Student[wielun, 666] sayhi
Stuent sing: 绿光
(5)接口转换

超集可以转换为子集,反过来不行

package main

import "fmt"

type Humaner interface { //子集
	sayhi()
}

type Personer interface { //超集
	Humaner //匿名字段,继承了sayhi
	sing(lrc string)
}

// Student实现了此方法
type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
	fmt.Println("Stuent sing:", lrc)
}

func main() {
	var iPro Personer //超集
	iPro = &Student{"wielun", 123}
	var i Humaner //子集
	i = iPro      //超集转换为子集
	i.sayhi()
}

结果:

Student[wielun, 123] sayhi
(6)空接口

空接口万能类型,保存任意类型的值

package main

import "fmt"

func main() {
	var i interface{} = 1
	fmt.Println("i = ", i)
}

结果:

i =  1
(7)类型断言(if)
package main

import "fmt"

type Student struct {
	name string
	id   int
}

func main() {
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello go"
	i[2] = Student{"wielun", 123}

	// 类型查询,类型断言
	for index, data := range i {
		if value, ok := data.(int); ok == true {
			fmt.Printf("x[%d] 类型为int, 内容为%d\n", index, value)
		} else if value, ok := data.(string); ok == true {
			fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value)
		} else if value, ok := data.(Student); ok == true {
			fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id)
		}
	}
}

结果:

x[0] 类型为int, 内容为1
x[1] 类型为string, 内容为hello go
x[2] 类型为Student, 内容为name = wielun, id = 123
(8)类型断言(switch)
package main

import "fmt"

type Student struct {
	name string
	id   int
}

func main() {
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello go"
	i[2] = Student{"wielun", 123}

	for index, data := range i {
		switch value := data.(type) {
		case int:
			fmt.Printf("x[%d] 类型为int, 内容为%d\n", index, value)
		case string:
			fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value)
		case Student:
			fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id)
		}
	}
}

结果:

x[0] 类型为int, 内容为1
x[1] 类型为string, 内容为hello go
x[2] 类型为Student, 内容为name = wielun, id = 123

二、异常处理


1、 error接口

(1)error接口的使用
package main

import (
	"errors"
	"fmt"
)

func main() {
	err1 := fmt.Errorf("%s", "this is normal err1")
	fmt.Println("err1 = ", err1)

	err2 := errors.New("this is normal err2")
	fmt.Println("err2 = ", err2)
}

结果:

err1 =  this is normal err1
err2 =  this is normal err2
(2)error接口应用
package main

import (
	"errors"
	"fmt"
)

func MyDiv(a, b int) (result int, err error) {
	if b == 0 {
		err = errors.New("分母不能为0")
		return
	} else {
		result = a / b
	}
	return

}

func main() {
	result, err := MyDiv(10, 0)
	if err != nil {
		fmt.Println("err = ", err)
	} else {
		fmt.Println("result = ", result)
	}
}

结果:

err =  分母不能为0

2、panic

(1)显式调用panic函数
package main

import "fmt"

func testa() {
	fmt.Println("aaaaaaaaa")
}

func testb() {
	fmt.Println("bbbbbbbbb")
	panic("this is a panic test")
}

func testc() {
	fmt.Println("cccccccccccccc")
}

func main() {
	testa()
	testb()
	testc()
}

结果:

aaaaaaaaa
bbbbbbbbb
panic: this is a panic test

goroutine 1 [running]:
main.testb(...)
        E:/MyWork/Go Projects/src/wielun666@163.com/05/03_显式调用pannic函数.go:11
main.main()
        E:/MyWork/Go Projects/src/wielun666@163.com/05/03_显式调用pannic函数.go:20 +0xf5
exit status 2
(2)数组越界导致panic
package main

import "fmt"

func testa() {
	fmt.Println("aaaaaaaaa")
}

func testb(x int) {
	var a [10]int
	a[x] = 111
}

func testc() {
	fmt.Println("cccccccccccccc")
}

func main() {
	testa()
	testb(20)
	testc()
}

结果:

aaaaaaaaa
panic: runtime error: index out of range [20] with length 10

goroutine 1 [running]:
main.testb(...)
        E:/MyWork/Go Projects/src/wielun666@163.com/05/04_数组越界导致panic.go:11
main.main()
        E:/MyWork/Go Projects/src/wielun666@163.com/05/04_数组越界导致panic.go:20 +0x8c
exit status 2

3、recover的使用

package main

import "fmt"

func testa() {
	fmt.Println("aaaaaaaaa")
}

func testb(x int) {
	// 设置recover
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	var a [10]int
	a[x] = 111
}

func testc() {
	fmt.Println("cccccccccccccc")
}

func main() {
	testa()
	testb(20)
	testc()
}

结果:

aaaaaaaaa
runtime error: index out of range [20] with length 10
cccccccccccccc

下一篇文章> Go入门(六)之文本文件处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wielun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值