本文省去基本定义


文章目录

  • 1. 函数
  • 1.1 闭包
  • 1.2 延迟执行语句(defer)
  • 1.3 处理运行时发生的错误
  • 1.4 宕机(panic)——程序终止运行
  • 1.5 宕机恢复(recover)——防止程序崩溃
  • 2. 结构体
  • 2.1 结构体的实例化
  • 2.2 方法
  • 2.3 结构体的继承
  • 2.4 实现接口
  • 2.5 函数和方法的区别


1. 函数

1.1 闭包

闭包是一个函数和其相关环境组合的一个整体
函数+引用环境=闭包
能通过指针调用匿名函数或者函数是实现闭包的关键

func main() {
	accumulate := Accumulate(1) //返回一个指向返回函数的指针
	fmt.Printf("%T", accumulate)
	fmt.Println(accumulate())//通过指针调用Accumulate内的匿名函数
	fmt.Println(accumulate())
}
func Accumulate(i int) func() int {
	return func() int {
		i++
		return i
	}
}

1.2 延迟执行语句(defer)

先被defer的语句最后被执行,最后被defer的语句,最先被执行。
用法: 使用延迟执行语句在函数退出时释放资源

func main() {
	defer fmt.Println("first")
	fmt.Println("second")
	defer fmt.Println("third")
}

golang结构体内部func取自己的成员变量 golang 结构体函数_Go

1.3 处理运行时发生的错误

Go语言的设计者认为其他语言的异常机制已被过度使用,上层逻辑需要为函数发生的异常付出太多的资源。
error是Go系统声明的接口类型,代码如下:

type error interface{
	Error() string
}

所有符合Error() string格式的方法,都能实现错误接口。Error()方法返回错误的具体描述,使用者可以通过这个字符串知道发生了什么错误。

func main() {
	fmt.Println(div(0, 1))
	
	fmt.Println(div(0, 0))
}
func div(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("made,除了个0!")
	}
	return a / b, nil
}

golang结构体内部func取自己的成员变量 golang 结构体函数_Go_02

1.4 宕机(panic)——程序终止运行

手动触发panic

func main() {
panic("触发异常。。。")
}

golang结构体内部func取自己的成员变量 golang 结构体函数_匿名函数_03

其次:

func main() {
	defer fmt.Println("beforepanic")
	panic("触发异常。。。")
	defer fmt.Println("afterpanic")
}

golang结构体内部func取自己的成员变量 golang 结构体函数_匿名函数_04


当panic()触发的宕机发生时,panic()后面的代码将不会被运行,但是在panic()函数前面已经运行过的defer语句依然会在宕机发生时发生作用。

1.5 宕机恢复(recover)——防止程序崩溃

这玩意真难用:
例1:

func main() {

	defer func() {
		//recover() //可以打印panic的错误信息
		//fmt.Println(recover())
		if err := recover(); err != nil { //产生了panic异常
			fmt.Println(err)
		}

	}() //别忘了(), 调用此匿名函数
	panic("0000")
}

golang结构体内部func取自己的成员变量 golang 结构体函数_实例化_05


例2:

package main

import "fmt"

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

func testb(x int) {
	//设置recover,recover只能放在defer后面使用
	defer func() {
		//recover() //可以打印panic的错误信息
		//fmt.Println(recover())
		if err := recover(); err != nil { //产生了panic异常
			fmt.Println(err)
		}

	}() //别忘了(), 调用此匿名函数
	var a [10]int
	a[x] = 111 //当x为20时候,导致数组越界,产生一个panic,导致程序崩溃
}

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

func main() {
	testa()
	testb(20) //当值是1的时候,就不会越界,值是20的时候,就会越界报错。
	testc()
}

golang结构体内部func取自己的成员变量 golang 结构体函数_golang_06

2. 结构体

2.1 结构体的实例化

  • 结构体本身是一种类型,可以像整型、字符串等类型一样,以var的方式声明结构体即可完成实例化。
type Student struct {
	name string
	age int
}

func main() {
	var student Student
	fmt.Printf("%T\n",student)
	fmt.Printf("%T\n",&student)
	
	student.name="lisi"
	student.age=11
	fmt.Println(student)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_go_07

  • Go语言中,还可以使用new关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。
func main() {
	student := new(Student)
	fmt.Printf("%T\n",student)
	fmt.Printf("%T\n",&student)

	student.name="lisi"
	student.age=11
	fmt.Println(student)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_Go_08


在Go语言中,访问结构体指针的成员变量时可以继续使用“.”。这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术,将ins.Name形式转换为(*ins).Name。

  • 在Go语言中,对结构体进行“&”取地址操作时,视为对该类型进行一次new的实例化操作。
func main() {
	//student是一个指针
	student := &Student{
		name: "zhansan",
		age:  0,
	}
	fmt.Println(student)
	fmt.Printf("%T\n", student)
	fmt.Printf("%T\n", &student)
	fmt.Printf("%T\n", *student)

	student.name = "lisi"
	student.age = 11
	fmt.Println(student)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_实例化_09

2.2 方法

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器(Receiver)。

这里多了一个接收器的概念:

接收器——方法作用的目标

golang结构体内部func取自己的成员变量 golang 结构体函数_实例化_10


重点区别一下:(student *Student)与(student Student)的区别

  1. 理解指针类型的接收器
    指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self。
    由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
func (this *Student) init() {
	this.name="init"
	this.age=21
}
func main() {
	var stu Student
	fmt.Println(stu)
	stu.init()
	fmt.Println(stu)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_匿名函数_11

func (this Student) initNoPointer() {
	this.name="noPointerInit"
	this.age=21
}
func main() {
	var stu Student
	fmt.Println(stu)
	stu.initNoPointer()
	fmt.Println(stu)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_golang_12

  1. 理解非指针类型的接收器
    当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
func (this Student) initNoPointerReturn() Student {
	this.name = "noPointerInit"
	this.age = 21 + this.age
	return this
}

func main() {
	var stu Student
	stu.age = 11
	fmt.Println(stu)
	initNoPointerReturn := stu.initNoPointerReturn()
	fmt.Println(initNoPointerReturn)
	fmt.Println(stu)
}

golang结构体内部func取自己的成员变量 golang 结构体函数_go_13


在计算机中,小对象由于值复制时的速度较快,所以适合使用非指针接收器。大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。

2.3 结构体的继承

组合思想

2.4 实现接口

想要实现接口只需要方法格式与接口内的方法结构一致即可


但是 Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 说明 对象的行为:如果谁能搞定这件事,它就可以用在这儿。

接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。

通过如下格式定义接口:

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
    ...
}

类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。

实现某个接口的类型(除了实现接口方法外)可以有其他的方法。

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

接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。


Go 语言规范定义了接口方法集的调用规则:

类型 T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
类型 *T 的可调用方法集包含接受者为 *T 的所有方法
类型 *T 的可调用方法集不包含接受者为 T 的方法

2.5 函数和方法的区别

函数将变量作为参数:Function1(recv)

方法在变量上被调用:recv.Method1()

在接收者是指针时,方法可以改变接收者的值(或状态),这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。

不要忘记 Method1 后边的括号 (),否则会引发编译器错误:method recv.Method1 is not an expression, must be called

接收者必须有一个显式的名字,这个名字必须在方法中被使用。

receiver_type 叫做 (接收者)基本类型,这个类型必须在和方法同样的包中被声明。

在 Go 中,(接收者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接收者来建立。

方法没有和数据定义(结构体)混在一起:它们是正交的类型;表示(数据)和行为(方法)是独立的。